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

319 lines
12 KiB
C#
Raw Permalink 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.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using AIImages.Models;
using RimWorld;
using Verse;
namespace AIImages.Services
{
/// <summary>
/// Сервис для извлечения и описания данных о внешности персонажа
/// </summary>
public class PawnDescriptionService : IPawnDescriptionService
{
public PawnAppearanceData ExtractAppearanceData(Pawn pawn)
{
if (pawn?.story == null)
return null;
var data = new PawnAppearanceData
{
Name = pawn.Name?.ToStringShort ?? "Unknown",
Gender = pawn.gender,
Age = pawn.ageTracker.AgeBiologicalYears,
BodyType = pawn.story.bodyType?.defName,
SkinColor = pawn.story.SkinColor,
HairStyle = pawn.story.hairDef?.label,
HairDefName = pawn.story.hairDef?.defName,
HairColor = pawn.story.HairColor,
};
// Извлекаем гены цвета кожи
if (pawn.genes?.GenesListForReading != null)
{
var skinColorGenes = pawn
.genes.GenesListForReading
.Where(gene => gene.Active && IsSkinColorGene(gene.def) && !IsGeneSuppressed(pawn, gene))
.ToList();
foreach (var gene in skinColorGenes)
{
data.SkinColorGeneDefNames.Add(gene.def.defName);
}
// Извлекаем гены цвета волос
var hairColorGenes = pawn
.genes.GenesListForReading
.Where(gene => gene.Active && IsHairColorGene(gene.def) && !IsGeneSuppressed(pawn, gene))
.ToList();
foreach (var gene in hairColorGenes)
{
data.HairColorGeneDefNames.Add(gene.def.defName);
}
}
// Извлекаем черты характера
if (pawn.story.traits?.allTraits != null)
{
data.Traits.AddRange(pawn.story.traits.allTraits);
}
// Извлекаем одежду
if (pawn.apparel?.WornApparel != null)
{
foreach (var apparel in pawn.apparel.WornApparel)
{
var apparelData = new ApparelData
{
Label = apparel.def.label,
DefName = apparel.def.defName,
Material = apparel.Stuff?.label,
MaterialDefName = apparel.Stuff?.defName,
Color = apparel.DrawColor,
LayerType = apparel.def.apparel?.LastLayer.ToString(),
Durability = apparel.HitPoints,
MaxDurability = apparel.MaxHitPoints,
};
if (apparel.TryGetQuality(out QualityCategory quality))
{
apparelData.Quality = quality;
}
data.Apparel.Add(apparelData);
}
}
return data;
}
public string GetAppearanceDescription(Pawn pawn)
{
if (pawn?.story == null)
return "AIImages.Appearance.NoInfo".Translate();
StringBuilder sb = new StringBuilder();
// Пол
sb.AppendLine("AIImages.Appearance.Gender".Translate(pawn.gender.GetLabel()));
// Возраст
sb.AppendLine("AIImages.Appearance.Age".Translate(pawn.ageTracker.AgeBiologicalYears));
// Тип тела
if (pawn.story.bodyType != null)
{
sb.AppendLine(
"AIImages.Appearance.BodyType".Translate(pawn.story.bodyType.defName)
);
}
// Цвет кожи (с умным описанием, сначала проверяем гены)
string skinDescription = ColorDescriptionService.GetSkinToneDescription(pawn);
if (!string.IsNullOrEmpty(skinDescription))
{
sb.AppendLine("AIImages.Appearance.SkinTone".Translate(skinDescription));
}
// Волосы
if (pawn.story.hairDef != null)
{
sb.AppendLine("AIImages.Appearance.Hairstyle".Translate(pawn.story.hairDef.label));
// Используем метод с проверкой генов
string hairColorDescription = ColorDescriptionService.GetHairColorDescription(pawn);
if (!string.IsNullOrEmpty(hairColorDescription))
{
sb.AppendLine(
"AIImages.Appearance.HairColorDesc".Translate(hairColorDescription)
);
}
}
// Черты характера
if (pawn.story.traits?.allTraits != null && pawn.story.traits.allTraits.Any())
{
sb.AppendLine("\n" + "AIImages.Appearance.Traits".Translate());
foreach (var trait in pawn.story.traits.allTraits)
{
sb.AppendLine($" • {trait.LabelCap}");
}
}
return sb.ToString();
}
public string GetApparelDescription(Pawn pawn)
{
if (pawn?.apparel == null)
return "AIImages.Apparel.NoInfo".Translate();
StringBuilder sb = new StringBuilder();
List<Apparel> wornApparel = pawn.apparel.WornApparel;
if (wornApparel == null || !wornApparel.Any())
{
sb.AppendLine("AIImages.Apparel.NoClothes".Translate());
}
else
{
sb.AppendLine("AIImages.Apparel.ListHeader".Translate(wornApparel.Count) + "\n");
foreach (Apparel apparel in wornApparel)
{
FormatApparelItem(sb, apparel);
}
}
return sb.ToString();
}
private void FormatApparelItem(StringBuilder sb, Apparel apparel)
{
sb.AppendLine($"• {apparel.LabelCap}");
if (apparel.TryGetQuality(out QualityCategory quality))
{
sb.AppendLine("AIImages.Apparel.Quality".Translate(quality.GetLabel()));
}
if (apparel.Stuff != null)
{
sb.AppendLine("AIImages.Apparel.Material".Translate(apparel.Stuff.LabelCap));
}
if (apparel.HitPoints < apparel.MaxHitPoints)
{
int percentage = (int)((float)apparel.HitPoints / apparel.MaxHitPoints * 100);
sb.AppendLine(
"AIImages.Apparel.Durability".Translate(
apparel.HitPoints,
apparel.MaxHitPoints,
percentage
)
);
}
if (apparel.DrawColor != UnityEngine.Color.white)
{
string colorDesc = ColorDescriptionService.GetApparelColorDescription(
apparel.DrawColor
);
sb.AppendLine("AIImages.Apparel.ColorDesc".Translate(colorDesc));
}
sb.AppendLine();
}
/// <summary>
/// Проверяет, является ли ген геном цвета кожи
/// </summary>
private bool IsSkinColorGene(GeneDef geneDef)
{
if (geneDef == null)
return false;
string geneDefName = geneDef.defName;
return geneDefName.StartsWith("Skin_")
|| geneDefName.Contains("SkinColor")
|| geneDefName.Contains("SkinTone");
}
/// <summary>
/// Проверяет, является ли ген геном цвета волос
/// </summary>
private 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 bool IsGeneSuppressed(Pawn pawn, Gene gene)
{
if (pawn?.genes == null || gene?.def == null)
return false;
// В RimWorld, если ген активен, он уже учтён в системе
// Но мы можем проверить, подавляет ли другой активный ген данный ген
try
{
// Проверяем все активные гены на предмет подавления данного гена
foreach (var otherGene in pawn.genes.GenesListForReading)
{
if (otherGene.Active && otherGene != gene && otherGene.def != null)
{
// Проверяем через reflection, так как свойство suppresses может быть приватным
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;
}
}
// Также проверяем свойство, если оно публичное
var suppressesProperty = otherGene.def.GetType().GetProperty("suppresses");
if (suppressesProperty != null)
{
var suppresses = suppressesProperty.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;
}
}
}