From 835bde4c6e22ccbd8983ff87193a4652e1f9b67a Mon Sep 17 00:00:00 2001 From: Morb <14136326+Morb0@users.noreply.github.com> Date: Tue, 19 Sep 2023 23:56:10 +0300 Subject: [PATCH] Sex restriction for markings (#19894) * Add sex restriction to markings * Apply to existing systems --- .../Humanoid/HumanoidAppearanceSystem.cs | 5 +- ...manoidMarkingModifierBoundUserInterface.cs | 2 +- .../HumanoidMarkingModifierWindow.xaml.cs | 3 +- Content.Client/Humanoid/MarkingPicker.xaml.cs | 27 +++++- .../UI/HumanoidProfileEditor.xaml.cs | 7 +- .../HumanoidAppearanceSystem.Modifier.cs | 3 + .../Humanoid/HumanoidCharacterAppearance.cs | 3 +- .../Humanoid/Markings/MarkingManager.cs | 92 ++++++++++++++++++- .../Humanoid/Markings/MarkingPrototype.cs | 3 + .../Humanoid/Markings/MarkingsSet.cs | 34 +++++++ .../SharedHumanoidAppearanceSystem.cs | 5 +- .../SharedHumanoidMarkingModifierSystem.cs | 3 + .../Preferences/HumanoidCharacterProfile.cs | 2 +- 13 files changed, 169 insertions(+), 20 deletions(-) diff --git a/Content.Client/Humanoid/HumanoidAppearanceSystem.cs b/Content.Client/Humanoid/HumanoidAppearanceSystem.cs index 76f1aaaec8..0b37844c6e 100644 --- a/Content.Client/Humanoid/HumanoidAppearanceSystem.cs +++ b/Content.Client/Humanoid/HumanoidAppearanceSystem.cs @@ -170,11 +170,11 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem var facialHair = new Marking(profile.Appearance.FacialHairStyleId, new[] { facialHairColor }); - if (_markingManager.CanBeApplied(profile.Species, hair, _prototypeManager)) + if (_markingManager.CanBeApplied(profile.Species, profile.Sex, hair, _prototypeManager)) { markings.AddBack(MarkingCategories.Hair, hair); } - if (_markingManager.CanBeApplied(profile.Species, facialHair, _prototypeManager)) + if (_markingManager.CanBeApplied(profile.Species, profile.Sex, facialHair, _prototypeManager)) { markings.AddBack(MarkingCategories.FacialHair, facialHair); } @@ -192,6 +192,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem } markings.EnsureSpecies(profile.Species, profile.Appearance.SkinColor, _markingManager, _prototypeManager); + markings.EnsureSexes(profile.Sex, _markingManager); markings.EnsureDefault( profile.Appearance.SkinColor, profile.Appearance.EyeColor, diff --git a/Content.Client/Humanoid/HumanoidMarkingModifierBoundUserInterface.cs b/Content.Client/Humanoid/HumanoidMarkingModifierBoundUserInterface.cs index 286965f8b2..a1742219fb 100644 --- a/Content.Client/Humanoid/HumanoidMarkingModifierBoundUserInterface.cs +++ b/Content.Client/Humanoid/HumanoidMarkingModifierBoundUserInterface.cs @@ -42,7 +42,7 @@ public sealed class HumanoidMarkingModifierBoundUserInterface : BoundUserInterfa return; } - _window.SetState(cast.MarkingSet, cast.Species, cast.SkinColor, cast.CustomBaseLayers); + _window.SetState(cast.MarkingSet, cast.Species, cast.Sex, cast.SkinColor, cast.CustomBaseLayers); } private void SendMarkingSet(MarkingSet set) diff --git a/Content.Client/Humanoid/HumanoidMarkingModifierWindow.xaml.cs b/Content.Client/Humanoid/HumanoidMarkingModifierWindow.xaml.cs index ea39c501b3..8cf90c4e63 100644 --- a/Content.Client/Humanoid/HumanoidMarkingModifierWindow.xaml.cs +++ b/Content.Client/Humanoid/HumanoidMarkingModifierWindow.xaml.cs @@ -63,6 +63,7 @@ public sealed partial class HumanoidMarkingModifierWindow : DefaultWindow public void SetState( MarkingSet markings, string species, + Sex sex, Color skinColor, Dictionary info ) @@ -84,7 +85,7 @@ public sealed partial class HumanoidMarkingModifierWindow : DefaultWindow eyesColor = eyes.Color.Value; } - MarkingPickerWidget.SetData(markings, species, skinColor, eyesColor); + MarkingPickerWidget.SetData(markings, species, sex, skinColor, eyesColor); } private sealed class HumanoidBaseLayerModifier : BoxContainer diff --git a/Content.Client/Humanoid/MarkingPicker.xaml.cs b/Content.Client/Humanoid/MarkingPicker.xaml.cs index 9df4e28655..43333439f0 100644 --- a/Content.Client/Humanoid/MarkingPicker.xaml.cs +++ b/Content.Client/Humanoid/MarkingPicker.xaml.cs @@ -35,6 +35,7 @@ public sealed partial class MarkingPicker : Control private List _markingCategories = Enum.GetValues().ToList(); private string _currentSpecies = SharedHumanoidAppearanceSystem.DefaultSpecies; + private Sex _currentSex = Sex.Unsexed; public Color CurrentSkinColor = Color.White; public Color CurrentEyeColor = Color.Black; public Marking? HairMarking; @@ -77,7 +78,7 @@ public sealed partial class MarkingPicker : Control } } - public void SetData(List newMarkings, string species, Color skinColor, Color eyeColor) + public void SetData(List newMarkings, string species, Sex sex, Color skinColor, Color eyeColor) { var pointsProto = _prototypeManager .Index(species).MarkingPoints; @@ -89,6 +90,7 @@ public sealed partial class MarkingPicker : Control } _currentSpecies = species; + _currentSex = sex; CurrentSkinColor = skinColor; CurrentEyeColor = eyeColor; @@ -96,7 +98,7 @@ public sealed partial class MarkingPicker : Control PopulateUsed(); } - public void SetData(MarkingSet set, string species, Color skinColor, Color eyeColor) + public void SetData(MarkingSet set, string species, Sex sex, Color skinColor, Color eyeColor) { _currentMarkings = set; @@ -106,6 +108,7 @@ public sealed partial class MarkingPicker : Control } _currentSpecies = species; + _currentSex = sex; CurrentSkinColor = skinColor; CurrentEyeColor = eyeColor; @@ -182,8 +185,8 @@ public sealed partial class MarkingPicker : Control _selectedUnusedMarking = null; var markings = IgnoreSpecies - ? _markingManager.MarkingsByCategory(_selectedMarkingCategory) - : _markingManager.MarkingsByCategoryAndSpecies(_selectedMarkingCategory, _currentSpecies); + ? _markingManager.MarkingsByCategoryAndSex(_selectedMarkingCategory, _currentSex) + : _markingManager.MarkingsByCategoryAndSpeciesAndSex(_selectedMarkingCategory, _currentSpecies, _currentSex); var sortedMarkings = markings.Values.Where(m => m.ID.ToLower().Contains(filter.ToLower()) || @@ -319,6 +322,22 @@ public sealed partial class MarkingPicker : Control _currentMarkings = new(markingList, speciesPrototype.MarkingPoints, _markingManager, _prototypeManager); _currentMarkings.EnsureSpecies(species, null, _markingManager); + _currentMarkings.EnsureSexes(_currentSex, _markingManager); + + Populate(CMarkingSearch.Text); + PopulateUsed(); + } + + public void SetSex(Sex sex) + { + _currentSex = sex; + var markingList = _currentMarkings.GetForwardEnumerator().ToList(); + + var speciesPrototype = _prototypeManager.Index(_currentSpecies); + + _currentMarkings = new(markingList, speciesPrototype.MarkingPoints, _markingManager, _prototypeManager); + _currentMarkings.EnsureSpecies(_currentSpecies, null, _markingManager); + _currentMarkings.EnsureSexes(_currentSex, _markingManager); Populate(CMarkingSearch.Text); PopulateUsed(); diff --git a/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs index 59ce6fe8fc..59e08d344f 100644 --- a/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs +++ b/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs @@ -748,6 +748,7 @@ namespace Content.Client.Preferences.UI break; } UpdateGenderControls(); + CMarkings.SetSex(newSex); IsDirty = true; } @@ -917,7 +918,7 @@ namespace Content.Client.Preferences.UI } CMarkings.SetData(Profile.Appearance.Markings, Profile.Species, - Profile.Appearance.SkinColor, Profile.Appearance.EyeColor + Profile.Sex, Profile.Appearance.SkinColor, Profile.Appearance.EyeColor ); } @@ -1002,7 +1003,7 @@ namespace Content.Client.Preferences.UI _markingManager.Markings.TryGetValue(Profile.Appearance.HairStyleId, out var hairProto) ) { - if (_markingManager.CanBeApplied(Profile.Species, hairProto, _prototypeManager)) + if (_markingManager.CanBeApplied(Profile.Species, Profile.Sex, hairProto, _prototypeManager)) { if (_markingManager.MustMatchSkin(Profile.Species, HumanoidVisualLayers.Hair, out var _, _prototypeManager)) { @@ -1037,7 +1038,7 @@ namespace Content.Client.Preferences.UI _markingManager.Markings.TryGetValue(Profile.Appearance.FacialHairStyleId, out var facialHairProto) ) { - if (_markingManager.CanBeApplied(Profile.Species, facialHairProto, _prototypeManager)) + if (_markingManager.CanBeApplied(Profile.Species, Profile.Sex, facialHairProto, _prototypeManager)) { if (_markingManager.MustMatchSkin(Profile.Species, HumanoidVisualLayers.Hair, out var _, _prototypeManager)) { diff --git a/Content.Server/Humanoid/Systems/HumanoidAppearanceSystem.Modifier.cs b/Content.Server/Humanoid/Systems/HumanoidAppearanceSystem.Modifier.cs index e7c421d092..7c0bb7383b 100644 --- a/Content.Server/Humanoid/Systems/HumanoidAppearanceSystem.Modifier.cs +++ b/Content.Server/Humanoid/Systems/HumanoidAppearanceSystem.Modifier.cs @@ -37,6 +37,7 @@ public sealed partial class HumanoidAppearanceSystem uid, HumanoidMarkingModifierKey.Key, new HumanoidMarkingModifierState(component.MarkingSet, component.Species, + component.Sex, component.SkinColor, component.CustomBaseLayers )); @@ -70,6 +71,7 @@ public sealed partial class HumanoidAppearanceSystem uid, HumanoidMarkingModifierKey.Key, new HumanoidMarkingModifierState(component.MarkingSet, component.Species, + component.Sex, component.SkinColor, component.CustomBaseLayers )); @@ -94,6 +96,7 @@ public sealed partial class HumanoidAppearanceSystem uid, HumanoidMarkingModifierKey.Key, new HumanoidMarkingModifierState(component.MarkingSet, component.Species, + component.Sex, component.SkinColor, component.CustomBaseLayers )); diff --git a/Content.Shared/Humanoid/HumanoidCharacterAppearance.cs b/Content.Shared/Humanoid/HumanoidCharacterAppearance.cs index e50e55025c..1ffcd1870b 100644 --- a/Content.Shared/Humanoid/HumanoidCharacterAppearance.cs +++ b/Content.Shared/Humanoid/HumanoidCharacterAppearance.cs @@ -188,7 +188,7 @@ namespace Content.Shared.Humanoid return new(color.RByte, color.GByte, color.BByte); } - public static HumanoidCharacterAppearance EnsureValid(HumanoidCharacterAppearance appearance, string species) + public static HumanoidCharacterAppearance EnsureValid(HumanoidCharacterAppearance appearance, string species, Sex sex) { var hairStyleId = appearance.HairStyleId; var facialHairStyleId = appearance.FacialHairStyleId; @@ -223,6 +223,7 @@ namespace Content.Shared.Humanoid } markingSet.EnsureSpecies(species, skinColor, markingManager); + markingSet.EnsureSexes(sex, markingManager); } return new HumanoidCharacterAppearance( diff --git a/Content.Shared/Humanoid/Markings/MarkingManager.cs b/Content.Shared/Humanoid/Markings/MarkingManager.cs index 9a72f5eebc..68f7cfe362 100644 --- a/Content.Shared/Humanoid/Markings/MarkingManager.cs +++ b/Content.Shared/Humanoid/Markings/MarkingManager.cs @@ -66,6 +66,74 @@ namespace Content.Shared.Humanoid.Markings { continue; } + res.Add(key, marking); + } + + return res; + } + + /// + /// Markings by category and sex. + /// + /// + /// + /// + /// This is done per category, as enumerating over every single marking by species isn't useful. + /// Please make a pull request if you find a use case for that behavior. + /// + /// + public IReadOnlyDictionary MarkingsByCategoryAndSex(MarkingCategories category, + Sex sex) + { + var res = new Dictionary(); + + foreach (var (key, marking) in MarkingsByCategory(category)) + { + if (marking.SexRestriction != null && marking.SexRestriction != sex) + { + continue; + } + + res.Add(key, marking); + } + + return res; + } + + /// + /// Markings by category, species and sex. + /// + /// + /// + /// + /// + /// This is done per category, as enumerating over every single marking by species isn't useful. + /// Please make a pull request if you find a use case for that behavior. + /// + /// + public IReadOnlyDictionary MarkingsByCategoryAndSpeciesAndSex(MarkingCategories category, + string species, Sex sex) + { + var speciesProto = _prototypeManager.Index(species); + var onlyWhitelisted = _prototypeManager.Index(speciesProto.MarkingPoints).OnlyWhitelisted; + var res = new Dictionary(); + + foreach (var (key, marking) in MarkingsByCategory(category)) + { + if (onlyWhitelisted && marking.SpeciesRestrictions == null) + { + continue; + } + + if (marking.SpeciesRestrictions != null && !marking.SpeciesRestrictions.Contains(species)) + { + continue; + } + + if (marking.SexRestriction != null && marking.SexRestriction != sex) + { + continue; + } res.Add(key, marking); } @@ -84,8 +152,9 @@ namespace Content.Shared.Humanoid.Markings /// /// /// + /// /// - public bool IsValidMarking(Marking marking, MarkingCategories category, string species) + public bool IsValidMarking(Marking marking, MarkingCategories category, string species, Sex sex) { if (!TryGetMarking(marking, out var proto)) { @@ -93,7 +162,8 @@ namespace Content.Shared.Humanoid.Markings } if (proto.MarkingCategory != category || - proto.SpeciesRestrictions != null && !proto.SpeciesRestrictions.Contains(species)) + proto.SpeciesRestrictions != null && !proto.SpeciesRestrictions.Contains(species) || + proto.SexRestriction != null && proto.SexRestriction != sex) { return false; } @@ -121,7 +191,7 @@ namespace Content.Shared.Humanoid.Markings } } - public bool CanBeApplied(string species, Marking marking, IPrototypeManager? prototypeManager = null) + public bool CanBeApplied(string species, Sex sex, Marking marking, IPrototypeManager? prototypeManager = null) { IoCManager.Resolve(ref prototypeManager); @@ -143,10 +213,16 @@ namespace Content.Shared.Humanoid.Markings { return false; } + + if (prototype.SexRestriction != null && prototype.SexRestriction != sex) + { + return false; + } + return true; } - public bool CanBeApplied(string species, MarkingPrototype prototype, IPrototypeManager? prototypeManager = null) + public bool CanBeApplied(string species, Sex sex, MarkingPrototype prototype, IPrototypeManager? prototypeManager = null) { IoCManager.Resolve(ref prototypeManager); @@ -163,6 +239,12 @@ namespace Content.Shared.Humanoid.Markings { return false; } + + if (prototype.SexRestriction != null && prototype.SexRestriction != sex) + { + return false; + } + return true; } @@ -181,7 +263,7 @@ namespace Content.Shared.Humanoid.Markings alpha = 1f; return false; } - + alpha = sprite.LayerAlpha; return true; } diff --git a/Content.Shared/Humanoid/Markings/MarkingPrototype.cs b/Content.Shared/Humanoid/Markings/MarkingPrototype.cs index 14eb4e0d75..19cb1773c9 100644 --- a/Content.Shared/Humanoid/Markings/MarkingPrototype.cs +++ b/Content.Shared/Humanoid/Markings/MarkingPrototype.cs @@ -20,6 +20,9 @@ namespace Content.Shared.Humanoid.Markings [DataField("speciesRestriction")] public List? SpeciesRestrictions { get; private set; } + [DataField("sexRestriction")] + public Sex? SexRestriction { get; private set; } + [DataField("followSkinColor")] public bool FollowSkinColor { get; private set; } = false; diff --git a/Content.Shared/Humanoid/Markings/MarkingsSet.cs b/Content.Shared/Humanoid/Markings/MarkingsSet.cs index 3fc6942600..d389e19415 100644 --- a/Content.Shared/Humanoid/Markings/MarkingsSet.cs +++ b/Content.Shared/Humanoid/Markings/MarkingsSet.cs @@ -199,6 +199,40 @@ public sealed partial class MarkingSet } } + /// + /// Filters markings based on sex and it's restrictions in the marking's prototype from this marking set. + /// + /// The species to filter. + /// Marking manager. + public void EnsureSexes(Sex sex, MarkingManager? markingManager = null) + { + IoCManager.Resolve(ref markingManager); + + var toRemove = new List<(MarkingCategories category, string id)>(); + + foreach (var (category, list) in Markings) + { + foreach (var marking in list) + { + if (!markingManager.TryGetMarking(marking, out var prototype)) + { + toRemove.Add((category, marking.MarkingId)); + continue; + } + + if (prototype.SexRestriction != null && prototype.SexRestriction != sex) + { + toRemove.Add((category, marking.MarkingId)); + } + } + } + + foreach (var remove in toRemove) + { + Remove(remove.category, remove.id); + } + } + /// /// Ensures that all markings in this set are valid. /// diff --git a/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs b/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs index a4e3393c7e..e7e7b5a672 100644 --- a/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs +++ b/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs @@ -254,6 +254,7 @@ public abstract class SharedHumanoidAppearanceSystem : EntitySystem var oldSex = humanoid.Sex; humanoid.Sex = sex; + humanoid.MarkingSet.EnsureSexes(sex, _markingManager); RaiseLocalEvent(uid, new SexChangedEvent(oldSex, sex)); if (sync) @@ -308,13 +309,13 @@ public abstract class SharedHumanoidAppearanceSystem : EntitySystem ? profile.Appearance.SkinColor.WithAlpha(facialHairAlpha) : profile.Appearance.FacialHairColor; if (_markingManager.Markings.TryGetValue(profile.Appearance.HairStyleId, out var hairPrototype) && - _markingManager.CanBeApplied(profile.Species, hairPrototype, _prototypeManager)) + _markingManager.CanBeApplied(profile.Species, profile.Sex, hairPrototype, _prototypeManager)) { AddMarking(uid, profile.Appearance.HairStyleId, hairColor, false); } if (_markingManager.Markings.TryGetValue(profile.Appearance.FacialHairStyleId, out var facialHairPrototype) && - _markingManager.CanBeApplied(profile.Species, facialHairPrototype, _prototypeManager)) + _markingManager.CanBeApplied(profile.Species, profile.Sex, facialHairPrototype, _prototypeManager)) { AddMarking(uid, profile.Appearance.FacialHairStyleId, facialHairColor, false); } diff --git a/Content.Shared/Humanoid/SharedHumanoidMarkingModifierSystem.cs b/Content.Shared/Humanoid/SharedHumanoidMarkingModifierSystem.cs index 0c0dfc3174..bd0bad9670 100644 --- a/Content.Shared/Humanoid/SharedHumanoidMarkingModifierSystem.cs +++ b/Content.Shared/Humanoid/SharedHumanoidMarkingModifierSystem.cs @@ -45,18 +45,21 @@ public sealed class HumanoidMarkingModifierState : BoundUserInterfaceState public HumanoidMarkingModifierState( MarkingSet markingSet, string species, + Sex sex, Color skinColor, Dictionary customBaseLayers ) { MarkingSet = markingSet; Species = species; + Sex = sex; SkinColor = skinColor; CustomBaseLayers = customBaseLayers; } public MarkingSet MarkingSet { get; } public string Species { get; } + public Sex Sex { get; } public Color SkinColor { get; } public Color EyeColor { get; } public Color? HairColor { get; } diff --git a/Content.Shared/Preferences/HumanoidCharacterProfile.cs b/Content.Shared/Preferences/HumanoidCharacterProfile.cs index a736984433..de5ef76232 100644 --- a/Content.Shared/Preferences/HumanoidCharacterProfile.cs +++ b/Content.Shared/Preferences/HumanoidCharacterProfile.cs @@ -433,7 +433,7 @@ namespace Content.Shared.Preferences flavortext = FormattedMessage.RemoveMarkup(FlavorText); } - var appearance = HumanoidCharacterAppearance.EnsureValid(Appearance, Species); + var appearance = HumanoidCharacterAppearance.EnsureValid(Appearance, Species, Sex); var prefsUnavailableMode = PreferenceUnavailable switch {