mirror of
https://github.com/space-wizards/space-station-14.git
synced 2026-02-14 19:29:53 +01:00
194 lines
6.9 KiB
C#
194 lines
6.9 KiB
C#
using System.Linq;
|
|
using System.Numerics;
|
|
using Content.Shared.Body;
|
|
using Content.Shared.Humanoid.Markings;
|
|
using Content.Shared.Humanoid.Prototypes;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Random;
|
|
using Robust.Shared.Serialization;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Content.Shared.Humanoid;
|
|
|
|
[DataDefinition]
|
|
[Serializable, NetSerializable]
|
|
public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance, IEquatable<HumanoidCharacterAppearance>
|
|
{
|
|
[DataField]
|
|
public Color EyeColor { get; set; } = Color.Black;
|
|
|
|
[DataField]
|
|
public Color SkinColor { get; set; } = Color.FromHsv(new Vector4(0.07f, 0.2f, 1f, 1f));
|
|
|
|
[DataField]
|
|
public Dictionary<ProtoId<OrganCategoryPrototype>, Dictionary<HumanoidVisualLayers, List<Marking>>> Markings { get; set; } = new();
|
|
|
|
public HumanoidCharacterAppearance(
|
|
Color eyeColor,
|
|
Color skinColor,
|
|
Dictionary<ProtoId<OrganCategoryPrototype>, Dictionary<HumanoidVisualLayers, List<Marking>>> markings)
|
|
{
|
|
EyeColor = ClampColor(eyeColor);
|
|
SkinColor = ClampColor(skinColor);
|
|
Markings = markings;
|
|
}
|
|
|
|
public HumanoidCharacterAppearance(HumanoidCharacterAppearance other) :
|
|
this(other.EyeColor, other.SkinColor, new(other.Markings))
|
|
{
|
|
|
|
}
|
|
|
|
public HumanoidCharacterAppearance WithEyeColor(Color newColor)
|
|
{
|
|
return new(newColor, SkinColor, Markings);
|
|
}
|
|
|
|
public HumanoidCharacterAppearance WithSkinColor(Color newColor)
|
|
{
|
|
return new(EyeColor, newColor, Markings);
|
|
}
|
|
|
|
public HumanoidCharacterAppearance WithMarkings(Dictionary<ProtoId<OrganCategoryPrototype>, Dictionary<HumanoidVisualLayers, List<Marking>>> newMarkings)
|
|
{
|
|
return new(EyeColor, SkinColor, newMarkings);
|
|
}
|
|
|
|
public static HumanoidCharacterAppearance DefaultWithSpecies(ProtoId<SpeciesPrototype> species, Sex sex)
|
|
{
|
|
var protoMan = IoCManager.Resolve<IPrototypeManager>();
|
|
var speciesPrototype = protoMan.Index<SpeciesPrototype>(species);
|
|
var skinColoration = protoMan.Index(speciesPrototype.SkinColoration).Strategy;
|
|
var skinColor = skinColoration.InputType switch
|
|
{
|
|
SkinColorationStrategyInput.Unary => skinColoration.FromUnary(speciesPrototype.DefaultHumanSkinTone),
|
|
SkinColorationStrategyInput.Color => skinColoration.ClosestSkinColor(speciesPrototype.DefaultSkinTone),
|
|
_ => skinColoration.ClosestSkinColor(speciesPrototype.DefaultSkinTone),
|
|
};
|
|
|
|
var appearance = new HumanoidCharacterAppearance(
|
|
Color.Black,
|
|
skinColor,
|
|
new()
|
|
);
|
|
return EnsureValid(appearance, species, sex);
|
|
}
|
|
|
|
private static IReadOnlyList<Color> _realisticEyeColors = new List<Color>
|
|
{
|
|
Color.Brown,
|
|
Color.Gray,
|
|
Color.Azure,
|
|
Color.SteelBlue,
|
|
Color.Black
|
|
};
|
|
|
|
public static HumanoidCharacterAppearance Random(string species, Sex sex)
|
|
{
|
|
var random = IoCManager.Resolve<IRobustRandom>();
|
|
var markingManager = IoCManager.Resolve<MarkingManager>();
|
|
|
|
// TODO: Add random markings
|
|
|
|
var newEyeColor = random.Pick(_realisticEyeColors);
|
|
|
|
var protoMan = IoCManager.Resolve<IPrototypeManager>();
|
|
var skinType = protoMan.Index<SpeciesPrototype>(species).SkinColoration;
|
|
var strategy = protoMan.Index(skinType).Strategy;
|
|
|
|
var newSkinColor = strategy.InputType switch
|
|
{
|
|
SkinColorationStrategyInput.Unary => strategy.FromUnary(random.NextFloat(0f, 100f)),
|
|
SkinColorationStrategyInput.Color => strategy.ClosestSkinColor(new Color(random.NextFloat(1), random.NextFloat(1), random.NextFloat(1), 1)),
|
|
_ => strategy.ClosestSkinColor(new Color(random.NextFloat(1), random.NextFloat(1), random.NextFloat(1), 1)),
|
|
};
|
|
|
|
return new HumanoidCharacterAppearance(newEyeColor, newSkinColor, new());
|
|
}
|
|
|
|
public static Color ClampColor(Color color)
|
|
{
|
|
return new(color.RByte, color.GByte, color.BByte);
|
|
}
|
|
|
|
public static HumanoidCharacterAppearance EnsureValid(HumanoidCharacterAppearance appearance, ProtoId<SpeciesPrototype> species, Sex sex)
|
|
{
|
|
var eyeColor = ClampColor(appearance.EyeColor);
|
|
|
|
var proto = IoCManager.Resolve<IPrototypeManager>();
|
|
var markingManager = IoCManager.Resolve<MarkingManager>();
|
|
|
|
var skinColor = appearance.SkinColor;
|
|
var validatedMarkings = appearance.Markings.ShallowClone();
|
|
|
|
if (proto.TryIndex(species, out var speciesProto))
|
|
{
|
|
var strategy = proto.Index(speciesProto.SkinColoration).Strategy;
|
|
var organs = markingManager.GetOrgans(species);
|
|
skinColor = strategy.EnsureVerified(skinColor);
|
|
|
|
foreach (var (organ, markings) in appearance.Markings)
|
|
{
|
|
if (!organs.ContainsKey(organ))
|
|
validatedMarkings.Remove(organ);
|
|
}
|
|
|
|
foreach (var (organ, organProtoID) in organs)
|
|
{
|
|
if (!markingManager.TryGetMarkingData(organProtoID, out var organData))
|
|
{
|
|
validatedMarkings.Remove(organ);
|
|
continue;
|
|
}
|
|
|
|
var actualMarkings = appearance.Markings.GetValueOrDefault(organ)?.ShallowClone() ?? [];
|
|
|
|
markingManager.EnsureValidColors(actualMarkings);
|
|
markingManager.EnsureValidGroupAndSex(actualMarkings, organData.Value.Group, sex);
|
|
markingManager.EnsureValidLayers(actualMarkings, organData.Value.Layers);
|
|
markingManager.EnsureValidLimits(actualMarkings, organData.Value.Group, organData.Value.Layers, skinColor, eyeColor);
|
|
|
|
validatedMarkings[organ] = actualMarkings;
|
|
}
|
|
}
|
|
|
|
return new HumanoidCharacterAppearance(
|
|
eyeColor,
|
|
skinColor,
|
|
validatedMarkings);
|
|
}
|
|
|
|
public bool MemberwiseEquals(ICharacterAppearance maybeOther)
|
|
{
|
|
if (maybeOther is not HumanoidCharacterAppearance other) return false;
|
|
if (!EyeColor.Equals(other.EyeColor)) return false;
|
|
if (!SkinColor.Equals(other.SkinColor)) return false;
|
|
if (!MarkingManager.MarkingsAreEqual(Markings, other.Markings)) return false;
|
|
return true;
|
|
}
|
|
|
|
public bool Equals(HumanoidCharacterAppearance? other)
|
|
{
|
|
if (ReferenceEquals(null, other)) return false;
|
|
if (ReferenceEquals(this, other)) return true;
|
|
return EyeColor.Equals(other.EyeColor) &&
|
|
SkinColor.Equals(other.SkinColor) &&
|
|
MarkingManager.MarkingsAreEqual(Markings, other.Markings);
|
|
}
|
|
|
|
public override bool Equals(object? obj)
|
|
{
|
|
return ReferenceEquals(this, obj) || obj is HumanoidCharacterAppearance other && Equals(other);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return HashCode.Combine(EyeColor, SkinColor, Markings);
|
|
}
|
|
|
|
public HumanoidCharacterAppearance Clone()
|
|
{
|
|
return new(this);
|
|
}
|
|
}
|