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,
customBaseLayers,
profile.Appearance.SkinColor,
profile.Sex,
new(), // doesn't exist yet
markings.GetForwardEnumerator().ToList());
}

View File

@@ -36,7 +36,9 @@ public sealed class HumanoidVisualizerSystem : VisualizerSystem<HumanoidComponen
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)
{
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)
{
if (_currentMarkings.TryGetCategory(_selectedMarkingCategory, out var listing)
&& listing.Contains(marking.AsMarking()))
if (_currentMarkings.TryGetMarking(_selectedMarkingCategory, marking.ID, out _))
{
continue;
}

View File

@@ -7,7 +7,4 @@ namespace Content.Server.CharacterAppearance.Components;
public sealed class RandomHumanoidAppearanceComponent : Component
{
[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.CustomBaseLayers,
component.SkinColor,
component.Sex,
component.AllHiddenLayers.ToList(),
component.CurrentMarkings.GetForwardEnumerator().ToList());
}
@@ -50,11 +51,13 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
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))
{
LoadProfile(uid, HumanoidCharacterProfile.DefaultWithSpecies(humanoid.Species), humanoid);
return;
}
// Do this first, because profiles currently do not support custom base layers
foreach (var (layer, info) in startingSet.CustomBaseLayers)
{
@@ -63,7 +66,6 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
LoadProfile(uid, startingSet.Profile, humanoid);
}
}
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)
{
// 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;
}
var profile = HumanoidCharacterProfile.Random(component.IgnoredSpecies);
var profile = HumanoidCharacterProfile.RandomWithSpecies(humanoid.Species);
_humanoid.LoadProfile(uid, profile);
_humanoid.LoadProfile(uid, profile, humanoid);
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>
{
Color.Brown,

View File

@@ -12,11 +12,12 @@ public enum HumanoidVisualizerKey
[Serializable, NetSerializable]
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;
CustomBaseLayerInfo = customBaseLayerInfo;
SkinColor = skinColor;
Sex = sex;
LayerVisibility = layerVisibility;
Markings = markings;
}
@@ -24,11 +25,12 @@ public sealed class HumanoidVisualizerData : ICloneable
public string Species { get; }
public Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> CustomBaseLayerInfo { get; }
public Color SkinColor { get; }
public Sex Sex { get; }
public List<HumanoidVisualLayers> LayerVisibility { get; }
public List<Marking> Markings { get; }
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")]
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>
/// The limit of body markings that you can place on this species.
/// </summary>

View File

@@ -24,10 +24,11 @@ public abstract class SharedHumanoidSystem : EntitySystem
string species,
Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> customBaseLayer,
Color skinColor,
Sex sex,
List<HumanoidVisualLayers> visLayers,
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
// in this.

View File

@@ -15,11 +15,10 @@ public static class SkinColor
}
/// <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>
/// <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>
/// <exception cref="ArgumentException">Exception if the value is under 0 or over 100.</exception>
public static Color HumanSkinTone(int tone)
{
// 0 - 100, 0 being gold/yellowish and 100 being dark
@@ -31,10 +30,7 @@ public static class SkinColor
// 20 is 25 - 20 - 100
// 100 is 25 - 100 - 20
if (tone < 0 || tone > 100)
{
throw new ArgumentException("Skin tone value was under 0 or over 100.");
}
tone = Math.Clamp(tone, 0, 100);
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()
{
return new(
@@ -120,6 +125,33 @@ namespace Content.Shared.Preferences
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)
{
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))
.ToArray()
).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 gender = sex == Sex.Male ? Gender.Male : Gender.Female;

View File

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

View File

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