CharacterKit/ClothingLayers.cs

Utility class that defines clothing layer rules and normalization for the CharacterEditor. It computes consistent Under/Over slot pairs, fixes assets that declare upper-body slots on the inner layer for outerwear, checks compatibility between two clothing items, and adds items into a ClothingContainer while removing incompatible entries.

Reflection
using System;

namespace CharacterEditor;

/// <summary>Consistent layer slots for selection, fitting and native Dresser entries.</summary>
public static class ClothingLayers
{
	public const Clothing.Slots UpperBody = Clothing.Slots.Chest | Clothing.Slots.LeftArm | Clothing.Slots.RightArm;

	static (Clothing.Slots Under, Clothing.Slots Over) Normalize(Clothing.Slots under, Clothing.Slots over, bool outerwear)
	{
		// Some outerwear declares its upper-body slots on the inner layer. Correct
		// that contradiction without changing the source asset or its other slots.
		if (outerwear && (over & UpperBody) == 0)
		{
			over |= under & UpperBody;
			under &= ~UpperBody;
		}
		return (under, over);
	}

	public static (Clothing.Slots Under, Clothing.Slots Over) Slots(Clothing item) =>
		Normalize(item.SlotsUnder, item.SlotsOver, ClothingResources.IsOuterwear(item));

	internal static (Clothing.Slots Under, Clothing.Slots Over) Slots(WardrobeItem item)
	{
		Enum.TryParse<Clothing.Slots>(item.SlotsUnder, out var under);
		Enum.TryParse<Clothing.Slots>(item.SlotsOver, out var over);
		return Normalize(under, over, item.Category == "Outerwear");
	}

	public static bool IsOuterwear(Clothing item) => (Slots(item).Over & UpperBody) != 0;

	public static bool CanBeWornWith(Clothing first, Clothing second)
	{
		if (first is null || second is null) return true;
		if (first == second || (first.HasHumanSkin && second.HasHumanSkin)) return false;
		var a = Slots(first);
		var b = Slots(second);
		return (a.Under & b.Under) == 0 && (a.Over & b.Over) == 0;
	}

	/// <summary>Use the same effective slots when assembling a native clothing container.</summary>
	public static void Add(ClothingContainer container, Clothing item)
	{
		container.Clothing.RemoveAll(entry => !CanBeWornWith(entry.Clothing, item));
		container.Clothing.Add(new ClothingContainer.ClothingEntry { Clothing = item });
	}
}