Humanoid fixes (#11538)

* humanoid sexmorph sprite restoration

can't believe i broke sex/gender AGAIN

* fixes default species appearances with no profile, tweaks randomization to no longer randomize species

* A

* fixes an oops

#11494
This commit is contained in:
Flipp Syder
2022-09-26 12:46:57 -07:00
committed by GitHub
parent 66e9c1975a
commit 0e1a190e0e
14 changed files with 107 additions and 31 deletions

View File

@@ -57,6 +57,7 @@ public sealed class HumanoidSystem : SharedHumanoidSystem
profile.Species, profile.Species,
customBaseLayers, customBaseLayers,
profile.Appearance.SkinColor, profile.Appearance.SkinColor,
profile.Sex,
new(), // doesn't exist yet new(), // doesn't exist yet
markings.GetForwardEnumerator().ToList()); markings.GetForwardEnumerator().ToList());
} }

View File

@@ -36,7 +36,9 @@ public sealed class HumanoidVisualizerSystem : VisualizerSystem<HumanoidComponen
return; return;
} }
bool dirty = data.SkinColor != component.SkinColor; var dirty = data.SkinColor != component.SkinColor || data.Sex != component.Sex;
component.Sex = data.Sex;
if (data.CustomBaseLayerInfo.Count != 0) if (data.CustomBaseLayerInfo.Count != 0)
{ {
dirty |= MergeCustomBaseSprites(uid, baseSprites.Sprites, data.CustomBaseLayerInfo, component); dirty |= MergeCustomBaseSprites(uid, baseSprites.Sprites, data.CustomBaseLayerInfo, component);
@@ -442,6 +444,4 @@ public sealed class HumanoidVisualizerSystem : VisualizerSystem<HumanoidComponen
} }
} }
} }
} }

View File

@@ -179,8 +179,7 @@ public sealed partial class MarkingPicker : Control
foreach (var marking in markings.Values) foreach (var marking in markings.Values)
{ {
if (_currentMarkings.TryGetCategory(_selectedMarkingCategory, out var listing) if (_currentMarkings.TryGetMarking(_selectedMarkingCategory, marking.ID, out _))
&& listing.Contains(marking.AsMarking()))
{ {
continue; continue;
} }

View File

@@ -7,7 +7,4 @@ namespace Content.Server.CharacterAppearance.Components;
public sealed class RandomHumanoidAppearanceComponent : Component public sealed class RandomHumanoidAppearanceComponent : Component
{ {
[DataField("randomizeName")] public bool RandomizeName = true; [DataField("randomizeName")] public bool RandomizeName = true;
[DataField("ignoredSpecies", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<SpeciesPrototype>))]
public readonly HashSet<string> IgnoredSpecies = new();
} }

View File

@@ -39,6 +39,7 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
component.Species, component.Species,
component.CustomBaseLayers, component.CustomBaseLayers,
component.SkinColor, component.SkinColor,
component.Sex,
component.AllHiddenLayers.ToList(), component.AllHiddenLayers.ToList(),
component.CurrentMarkings.GetForwardEnumerator().ToList()); component.CurrentMarkings.GetForwardEnumerator().ToList());
} }
@@ -50,19 +51,20 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
return; return;
} }
SetSpecies(uid, humanoid.Species, false, humanoid); if (string.IsNullOrEmpty(humanoid.Initial)
|| !_prototypeManager.TryIndex(humanoid.Initial, out HumanoidProfilePrototype? startingSet))
if (!string.IsNullOrEmpty(humanoid.Initial)
&& _prototypeManager.TryIndex(humanoid.Initial, out HumanoidProfilePrototype? startingSet))
{ {
// Do this first, because profiles currently do not support custom base layers LoadProfile(uid, HumanoidCharacterProfile.DefaultWithSpecies(humanoid.Species), humanoid);
foreach (var (layer, info) in startingSet.CustomBaseLayers) return;
{
humanoid.CustomBaseLayers.Add(layer, info);
}
LoadProfile(uid, startingSet.Profile, humanoid);
} }
// Do this first, because profiles currently do not support custom base layers
foreach (var (layer, info) in startingSet.CustomBaseLayers)
{
humanoid.CustomBaseLayers.Add(layer, info);
}
LoadProfile(uid, startingSet.Profile, humanoid);
} }
private void OnExamined(EntityUid uid, HumanoidComponent component, ExaminedEvent args) private void OnExamined(EntityUid uid, HumanoidComponent component, ExaminedEvent args)

View File

@@ -17,14 +17,14 @@ public sealed class RandomHumanoidAppearanceSystem : EntitySystem
private void OnMapInit(EntityUid uid, RandomHumanoidAppearanceComponent component, MapInitEvent args) private void OnMapInit(EntityUid uid, RandomHumanoidAppearanceComponent component, MapInitEvent args)
{ {
// If we have an initial profile/base layer set, do not randomize this humanoid. // If we have an initial profile/base layer set, do not randomize this humanoid.
if (TryComp(uid, out HumanoidComponent? humanoid) && !string.IsNullOrEmpty(humanoid.Initial)) if (!TryComp(uid, out HumanoidComponent? humanoid) || !string.IsNullOrEmpty(humanoid.Initial))
{ {
return; return;
} }
var profile = HumanoidCharacterProfile.Random(component.IgnoredSpecies); var profile = HumanoidCharacterProfile.RandomWithSpecies(humanoid.Species);
_humanoid.LoadProfile(uid, profile); _humanoid.LoadProfile(uid, profile, humanoid);
if (component.RandomizeName) if (component.RandomizeName)
{ {

View File

@@ -97,6 +97,28 @@ namespace Content.Shared.Humanoid
); );
} }
public static HumanoidCharacterAppearance DefaultWithSpecies(string species)
{
var speciesPrototype = IoCManager.Resolve<IPrototypeManager>().Index<SpeciesPrototype>(species);
var skinColor = speciesPrototype.SkinColoration switch
{
HumanoidSkinColor.HumanToned => Humanoid.SkinColor.HumanSkinTone(speciesPrototype.DefaultHumanSkinTone),
HumanoidSkinColor.Hues => speciesPrototype.DefaultSkinTone,
HumanoidSkinColor.TintedHues => Humanoid.SkinColor.TintedHues(speciesPrototype.DefaultSkinTone),
_ => Humanoid.SkinColor.ValidHumanSkinTone
};
return new(
HairStyles.DefaultHairStyle,
Color.Black,
HairStyles.DefaultFacialHairStyle,
Color.Black,
Color.Black,
skinColor,
new ()
);
}
private static IReadOnlyList<Color> RealisticEyeColors = new List<Color> private static IReadOnlyList<Color> RealisticEyeColors = new List<Color>
{ {
Color.Brown, Color.Brown,

View File

@@ -12,11 +12,12 @@ public enum HumanoidVisualizerKey
[Serializable, NetSerializable] [Serializable, NetSerializable]
public sealed class HumanoidVisualizerData : ICloneable public sealed class HumanoidVisualizerData : ICloneable
{ {
public HumanoidVisualizerData(string species, Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> customBaseLayerInfo, Color skinColor, List<HumanoidVisualLayers> layerVisibility, List<Marking> markings) public HumanoidVisualizerData(string species, Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> customBaseLayerInfo, Color skinColor, Sex sex, List<HumanoidVisualLayers> layerVisibility, List<Marking> markings)
{ {
Species = species; Species = species;
CustomBaseLayerInfo = customBaseLayerInfo; CustomBaseLayerInfo = customBaseLayerInfo;
SkinColor = skinColor; SkinColor = skinColor;
Sex = sex;
LayerVisibility = layerVisibility; LayerVisibility = layerVisibility;
Markings = markings; Markings = markings;
} }
@@ -24,11 +25,12 @@ public sealed class HumanoidVisualizerData : ICloneable
public string Species { get; } public string Species { get; }
public Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> CustomBaseLayerInfo { get; } public Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> CustomBaseLayerInfo { get; }
public Color SkinColor { get; } public Color SkinColor { get; }
public Sex Sex { get; }
public List<HumanoidVisualLayers> LayerVisibility { get; } public List<HumanoidVisualLayers> LayerVisibility { get; }
public List<Marking> Markings { get; } public List<Marking> Markings { get; }
public object Clone() public object Clone()
{ {
return new HumanoidVisualizerData(Species, new(CustomBaseLayerInfo), SkinColor, new(LayerVisibility), new(Markings)); return new HumanoidVisualizerData(Species, new(CustomBaseLayerInfo), SkinColor, Sex, new(LayerVisibility), new(Markings));
} }
} }

View File

@@ -44,6 +44,19 @@ public sealed class SpeciesPrototype : IPrototype
[DataField("sprites")] [DataField("sprites")]
public string SpriteSet { get; } = default!; public string SpriteSet { get; } = default!;
/// <summary>
/// Default skin tone for this species. This applies for non-human skin tones.
/// </summary>
[DataField("defaultSkinTone")]
public Color DefaultSkinTone { get; } = Color.White;
/// <summary>
/// Default human skin tone for this species. This applies for human skin tones.
/// See <see cref="SkinColor.HumanSkinTone"/> for the valid range of skin tones.
/// </summary>
[DataField("defaultHumanSkinTone")]
public int DefaultHumanSkinTone { get; } = 20;
/// <summary> /// <summary>
/// The limit of body markings that you can place on this species. /// The limit of body markings that you can place on this species.
/// </summary> /// </summary>

View File

@@ -24,10 +24,11 @@ public abstract class SharedHumanoidSystem : EntitySystem
string species, string species,
Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> customBaseLayer, Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> customBaseLayer,
Color skinColor, Color skinColor,
Sex sex,
List<HumanoidVisualLayers> visLayers, List<HumanoidVisualLayers> visLayers,
List<Marking> markings) List<Marking> markings)
{ {
var data = new HumanoidVisualizerData(species, customBaseLayer, skinColor, visLayers, markings); var data = new HumanoidVisualizerData(species, customBaseLayer, skinColor, sex, visLayers, markings);
// Locally raise an event for this, because there might be some systems interested // Locally raise an event for this, because there might be some systems interested
// in this. // in this.

View File

@@ -15,11 +15,10 @@ public static class SkinColor
} }
/// <summary> /// <summary>
/// Get a human skin tone based on a scale of 0 to 100. /// Get a human skin tone based on a scale of 0 to 100. The value is clamped between 0 and 100.
/// </summary> /// </summary>
/// <param name="tone">Skin tone. Valid range is 0 to 100, inclusive. 0 is gold/yellowish, 100 is dark brown.</param> /// <param name="tone">Skin tone. Valid range is 0 to 100, inclusive. 0 is gold/yellowish, 100 is dark brown.</param>
/// <returns>A human skin tone.</returns> /// <returns>A human skin tone.</returns>
/// <exception cref="ArgumentException">Exception if the value is under 0 or over 100.</exception>
public static Color HumanSkinTone(int tone) public static Color HumanSkinTone(int tone)
{ {
// 0 - 100, 0 being gold/yellowish and 100 being dark // 0 - 100, 0 being gold/yellowish and 100 being dark
@@ -31,10 +30,7 @@ public static class SkinColor
// 20 is 25 - 20 - 100 // 20 is 25 - 20 - 100
// 100 is 25 - 100 - 20 // 100 is 25 - 100 - 20
if (tone < 0 || tone > 100) tone = Math.Clamp(tone, 0, 100);
{
throw new ArgumentException("Skin tone value was under 0 or over 100.");
}
var rangeOffset = tone - 20; var rangeOffset = tone - 20;

View File

@@ -99,6 +99,11 @@ namespace Content.Shared.Preferences
{ {
} }
/// <summary>
/// Get the default humanoid character profile, using internal constant values.
/// Defaults to <see cref="SharedHumanoidSystem.DefaultSpecies"/> for the species.
/// </summary>
/// <returns></returns>
public static HumanoidCharacterProfile Default() public static HumanoidCharacterProfile Default()
{ {
return new( return new(
@@ -120,6 +125,33 @@ namespace Content.Shared.Preferences
new List<string>()); new List<string>());
} }
/// <summary>
/// Return a default character profile, based on species.
/// </summary>
/// <param name="species">The species to use in this default profile. The default species is <see cref="SharedHumanoidSystem.DefaultSpecies"/>.</param>
/// <returns>Humanoid character profile with default settings.</returns>
public static HumanoidCharacterProfile DefaultWithSpecies(string species = SharedHumanoidSystem.DefaultSpecies)
{
return new(
"John Doe",
"",
species,
MinimumAge,
Sex.Male,
Gender.Male,
HumanoidCharacterAppearance.DefaultWithSpecies(species),
ClothingPreference.Jumpsuit,
BackpackPreference.Backpack,
new Dictionary<string, JobPriority>
{
{SharedGameTicker.FallbackOverflowJob, JobPriority.High}
},
PreferenceUnavailableMode.SpawnAsOverflow,
new List<string>(),
new List<string>());
}
// TODO: This should eventually not be a visual change only.
public static HumanoidCharacterProfile Random(HashSet<string>? ignoredSpecies = null) public static HumanoidCharacterProfile Random(HashSet<string>? ignoredSpecies = null)
{ {
var prototypeManager = IoCManager.Resolve<IPrototypeManager>(); var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
@@ -130,6 +162,15 @@ namespace Content.Shared.Preferences
.Where(x => ignoredSpecies == null ? x.RoundStart : x.RoundStart && !ignoredSpecies.Contains(x.ID)) .Where(x => ignoredSpecies == null ? x.RoundStart : x.RoundStart && !ignoredSpecies.Contains(x.ID))
.ToArray() .ToArray()
).ID; ).ID;
return RandomWithSpecies(species);
}
public static HumanoidCharacterProfile RandomWithSpecies(string species = SharedHumanoidSystem.DefaultSpecies)
{
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
var random = IoCManager.Resolve<IRobustRandom>();
var sex = random.Prob(0.5f) ? Sex.Male : Sex.Female; var sex = random.Prob(0.5f) ? Sex.Male : Sex.Female;
var gender = sex == Sex.Male ? Gender.Male : Gender.Female; var gender = sex == Sex.Male ? Gender.Male : Gender.Female;

View File

@@ -4,6 +4,7 @@
roundStart: true roundStart: true
prototype: MobReptilian prototype: MobReptilian
sprites: MobReptilianSprites sprites: MobReptilianSprites
defaultSkinTone: "#34a223"
markingLimits: MobReptilianMarkingLimits markingLimits: MobReptilianMarkingLimits
dollPrototype: MobReptilianDummy dollPrototype: MobReptilianDummy
skinColoration: Hues skinColoration: Hues

View File

@@ -4,6 +4,7 @@
roundStart: true roundStart: true
prototype: MobSlimePerson prototype: MobSlimePerson
sprites: MobSlimeSprites sprites: MobSlimeSprites
defaultSkinTone: "#b8b8b8"
markingLimits: MobSlimeMarkingLimits markingLimits: MobSlimeMarkingLimits
dollPrototype: MobSlimePersonDummy dollPrototype: MobSlimePersonDummy
skinColoration: Hues skinColoration: Hues