Marking default coloring (#13039)
* Marking coloring WIP * EnsureDefault now supports coloring! * Now markings have coloring when they get added * Many things * yml files * cleanup * Some requested changes * Nullable type and WIP caching * Time to resolve that thing with deprecated hair fields * Latest reviews + im still trying to use these hair markings * FirstOrDefault thing and Tattoo docs * IDK * It's now works a bit more properly in preferences GUI * THEY SYNCING! However preferences GUI still broken and doesn't work properly * Markings now updating when changing in GUI. However they still don't work properly with bald humanoids * Forgor... * Default hair-colored markings will not color to hair if there is no hair * Fixed default colors for customizable markings * Fixed bug in prefs GUI that set current hair to null * Now markings that must match skin color because of limb (e.x. Slimes) - will match skin color * final tweaks: if hair uses skin color then markings will use skin color as hair color (slimes) * fix * fixed dirty. no more funni invis bug * Mirrors and client profile loading * default colors soon TM * review + better coloring * Hardcode is gone * diona markings * oh my god * fixed CategoryColoring * cool fallback, clean up and some other tweaks * code style * more style * a
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
@@ -42,6 +43,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
||||
component.PermanentlyHidden = new(state.PermanentlyHidden);
|
||||
|
||||
component.CustomBaseLayers = state.CustomBaseLayers.ShallowClone();
|
||||
|
||||
UpdateLayers(component, sprite);
|
||||
|
||||
ApplyMarkingSet(uid, state.Markings, component, sprite);
|
||||
@@ -134,22 +136,66 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
||||
var customBaseLayers = new Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo>();
|
||||
|
||||
var speciesPrototype = _prototypeManager.Index<SpeciesPrototype>(profile.Species);
|
||||
var markings = new MarkingSet(profile.Appearance.Markings, speciesPrototype.MarkingPoints, _markingManager,
|
||||
_prototypeManager);
|
||||
markings.EnsureDefault(profile.Appearance.SkinColor, _markingManager);
|
||||
var markings = new MarkingSet(speciesPrototype.MarkingPoints, _markingManager, _prototypeManager);
|
||||
|
||||
// Add markings that doesn't need coloring. We store them until we add all other markings that doesn't need it.
|
||||
var markingFColored = new Dictionary<Marking, MarkingPrototype>();
|
||||
foreach (var marking in profile.Appearance.Markings)
|
||||
{
|
||||
if (_markingManager.TryGetMarking(marking, out var prototype))
|
||||
{
|
||||
if (!prototype.ForcedColoring)
|
||||
{
|
||||
markings.AddBack(prototype.MarkingCategory, marking);
|
||||
}
|
||||
else
|
||||
{
|
||||
markingFColored.Add(marking, prototype);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// legacy: remove in the future?
|
||||
markings.RemoveCategory(MarkingCategories.Hair);
|
||||
markings.RemoveCategory(MarkingCategories.FacialHair);
|
||||
|
||||
var hair = new Marking(profile.Appearance.HairStyleId, new[] { profile.Appearance.HairColor });
|
||||
markings.AddBack(MarkingCategories.Hair, hair);
|
||||
//markings.RemoveCategory(MarkingCategories.Hair);
|
||||
//markings.RemoveCategory(MarkingCategories.FacialHair);
|
||||
|
||||
// We need to ensure hair before applying it or coloring can try depend on markings that can be invalid
|
||||
var hairColor = _markingManager.MustMatchSkin(profile.Species, HumanoidVisualLayers.Hair, _prototypeManager)
|
||||
? profile.Appearance.SkinColor : profile.Appearance.HairColor;
|
||||
var hair = new Marking(profile.Appearance.HairStyleId,
|
||||
new[] { hairColor });
|
||||
|
||||
var facialHairColor = _markingManager.MustMatchSkin(profile.Species, HumanoidVisualLayers.FacialHair, _prototypeManager)
|
||||
? profile.Appearance.SkinColor : profile.Appearance.FacialHairColor;
|
||||
var facialHair = new Marking(profile.Appearance.FacialHairStyleId,
|
||||
new[] { profile.Appearance.FacialHairColor });
|
||||
markings.AddBack(MarkingCategories.FacialHair, facialHair);
|
||||
new[] { facialHairColor });
|
||||
|
||||
markings.FilterSpecies(profile.Species, _markingManager, _prototypeManager);
|
||||
if (_markingManager.CanBeApplied(profile.Species, hair, _prototypeManager))
|
||||
{
|
||||
markings.AddBack(MarkingCategories.Hair, hair);
|
||||
}
|
||||
if (_markingManager.CanBeApplied(profile.Species, facialHair, _prototypeManager))
|
||||
{
|
||||
markings.AddBack(MarkingCategories.FacialHair, facialHair);
|
||||
}
|
||||
|
||||
// Finally adding marking with forced colors
|
||||
foreach (var (marking, prototype) in markingFColored)
|
||||
{
|
||||
var markingColors = MarkingColoring.GetMarkingLayerColors(
|
||||
prototype,
|
||||
profile.Appearance.SkinColor,
|
||||
profile.Appearance.EyeColor,
|
||||
markings
|
||||
);
|
||||
markings.AddBack(prototype.MarkingCategory, new Marking(marking.MarkingId, markingColors));
|
||||
}
|
||||
|
||||
markings.EnsureSpecies(profile.Species, profile.Appearance.SkinColor, _markingManager, _prototypeManager);
|
||||
markings.EnsureDefault(
|
||||
profile.Appearance.SkinColor,
|
||||
profile.Appearance.EyeColor,
|
||||
_markingManager);
|
||||
|
||||
DebugTools.Assert(uid.IsClientSide());
|
||||
|
||||
@@ -239,7 +285,6 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
||||
spriteComp.RemoveLayer(index);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyMarking(EntityUid uid,
|
||||
MarkingPrototype markingPrototype,
|
||||
IReadOnlyList<Color>? colors,
|
||||
@@ -273,22 +318,19 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
||||
}
|
||||
|
||||
sprite.LayerSetVisible(layerId, visible);
|
||||
|
||||
|
||||
if (!visible || setting == null) // this is kinda implied
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (markingPrototype.FollowSkinColor || colors == null || setting.MarkingsMatchSkin)
|
||||
if (colors != null)
|
||||
{
|
||||
var skinColor = humanoid.SkinColor;
|
||||
skinColor.A = setting.LayerAlpha;
|
||||
|
||||
sprite.LayerSetColor(layerId, skinColor);
|
||||
sprite.LayerSetColor(layerId, colors[j]);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetColor(layerId, colors[j]);
|
||||
sprite.LayerSetColor(layerId, Color.White);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,11 +60,13 @@ public sealed partial class HumanoidMarkingModifierWindow : DefaultWindow
|
||||
string? state = _protoMan.HasIndex<HumanoidSpeciesSpriteLayer>(modifier.Text) ? modifier.Text : null;
|
||||
OnLayerInfoModified?.Invoke(layer, new CustomBaseLayerInfo(state, modifier.Color));
|
||||
}
|
||||
|
||||
public void SetState(MarkingSet markings, string species, Color skinColor, Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> info)
|
||||
public void SetState(
|
||||
MarkingSet markings,
|
||||
string species,
|
||||
Color skinColor,
|
||||
Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> info
|
||||
)
|
||||
{
|
||||
MarkingPickerWidget.SetData(markings, species, skinColor);
|
||||
|
||||
foreach (var (layer, modifier) in _modifiers)
|
||||
{
|
||||
if (!info.TryGetValue(layer, out var layerInfo))
|
||||
@@ -75,6 +77,14 @@ public sealed partial class HumanoidMarkingModifierWindow : DefaultWindow
|
||||
|
||||
modifier.SetState(true, layerInfo.ID ?? string.Empty, layerInfo.Color ?? Color.White);
|
||||
}
|
||||
|
||||
var eyesColor = Color.White;
|
||||
if (info.TryGetValue(HumanoidVisualLayers.Eyes, out var eyes) && eyes.Color != null)
|
||||
{
|
||||
eyesColor = eyes.Color.Value;
|
||||
}
|
||||
|
||||
MarkingPickerWidget.SetData(markings, species, skinColor, eyesColor);
|
||||
}
|
||||
|
||||
private sealed class HumanoidBaseLayerModifier : BoxContainer
|
||||
|
||||
@@ -36,6 +36,9 @@ public sealed partial class MarkingPicker : Control
|
||||
|
||||
private string _currentSpecies = SharedHumanoidAppearanceSystem.DefaultSpecies;
|
||||
public Color CurrentSkinColor = Color.White;
|
||||
public Color CurrentEyeColor = Color.Black;
|
||||
public Marking? HairMarking;
|
||||
public Marking? FacialHairMarking;
|
||||
|
||||
private readonly HashSet<MarkingCategories> _ignoreCategories = new();
|
||||
|
||||
@@ -74,7 +77,7 @@ public sealed partial class MarkingPicker : Control
|
||||
}
|
||||
}
|
||||
|
||||
public void SetData(List<Marking> newMarkings, string species, Color skinColor)
|
||||
public void SetData(List<Marking> newMarkings, string species, Color skinColor, Color eyeColor)
|
||||
{
|
||||
var pointsProto = _prototypeManager
|
||||
.Index<SpeciesPrototype>(species).MarkingPoints;
|
||||
@@ -82,33 +85,36 @@ public sealed partial class MarkingPicker : Control
|
||||
|
||||
if (!IgnoreSpecies)
|
||||
{
|
||||
_currentMarkings.FilterSpecies(species); // should be validated server-side but it can't hurt
|
||||
_currentMarkings.EnsureSpecies(species, skinColor, _markingManager); // should be validated server-side but it can't hurt
|
||||
}
|
||||
|
||||
_currentSpecies = species;
|
||||
CurrentSkinColor = skinColor;
|
||||
CurrentEyeColor = eyeColor;
|
||||
|
||||
Populate();
|
||||
PopulateUsed();
|
||||
}
|
||||
|
||||
public void SetData(MarkingSet set, string species, Color skinColor)
|
||||
public void SetData(MarkingSet set, string species, Color skinColor, Color eyeColor)
|
||||
{
|
||||
_currentMarkings = set;
|
||||
|
||||
if (!IgnoreSpecies)
|
||||
{
|
||||
_currentMarkings.FilterSpecies(species); // should be validated server-side but it can't hurt
|
||||
_currentMarkings.EnsureSpecies(species, skinColor, _markingManager); // should be validated server-side but it can't hurt
|
||||
}
|
||||
|
||||
_currentSpecies = species;
|
||||
CurrentSkinColor = skinColor;
|
||||
CurrentEyeColor = eyeColor;
|
||||
|
||||
Populate();
|
||||
PopulateUsed();
|
||||
}
|
||||
|
||||
public void SetSkinColor(Color color) => CurrentSkinColor = color;
|
||||
public void SetEyeColor(Color color) => CurrentEyeColor = color;
|
||||
|
||||
public MarkingPicker()
|
||||
{
|
||||
@@ -201,7 +207,7 @@ public sealed partial class MarkingPicker : Control
|
||||
|
||||
if (!IgnoreSpecies)
|
||||
{
|
||||
_currentMarkings.FilterSpecies(_currentSpecies, _markingManager);
|
||||
_currentMarkings.EnsureSpecies(_currentSpecies, null, _markingManager);
|
||||
}
|
||||
|
||||
// walk backwards through the list for visual purposes
|
||||
@@ -305,7 +311,7 @@ public sealed partial class MarkingPicker : Control
|
||||
var speciesPrototype = _prototypeManager.Index<SpeciesPrototype>(species);
|
||||
|
||||
_currentMarkings = new(markingList, speciesPrototype.MarkingPoints, _markingManager, _prototypeManager);
|
||||
_currentMarkings.FilterSpecies(species);
|
||||
_currentMarkings.EnsureSpecies(species, null, _markingManager);
|
||||
|
||||
Populate();
|
||||
PopulateUsed();
|
||||
@@ -335,7 +341,7 @@ public sealed partial class MarkingPicker : Control
|
||||
_selectedMarking = CMarkingsUsed[item.ItemIndex];
|
||||
var prototype = (MarkingPrototype) _selectedMarking.Metadata!;
|
||||
|
||||
if (prototype.FollowSkinColor)
|
||||
if (prototype.ForcedColoring)
|
||||
{
|
||||
CMarkingColors.Visible = false;
|
||||
|
||||
@@ -412,12 +418,40 @@ public sealed partial class MarkingPicker : Control
|
||||
}
|
||||
|
||||
var marking = (MarkingPrototype) _selectedUnusedMarking.Metadata!;
|
||||
|
||||
|
||||
var markingObject = marking.AsMarking();
|
||||
for (var i = 0; i < markingObject.MarkingColors.Count; i++)
|
||||
|
||||
// We need add hair markings in cloned set manually because _currentMarkings doesn't have it
|
||||
var markingSet = new MarkingSet(_currentMarkings);
|
||||
if (HairMarking != null)
|
||||
{
|
||||
markingObject.SetColor(i, CurrentSkinColor);
|
||||
markingSet.AddBack(MarkingCategories.Hair, HairMarking);
|
||||
}
|
||||
if (FacialHairMarking != null)
|
||||
{
|
||||
markingSet.AddBack(MarkingCategories.FacialHair, FacialHairMarking);
|
||||
}
|
||||
|
||||
if (!_markingManager.MustMatchSkin(_currentSpecies, marking.BodyPart, _prototypeManager))
|
||||
{
|
||||
// Do default coloring
|
||||
var colors = MarkingColoring.GetMarkingLayerColors(
|
||||
marking,
|
||||
CurrentSkinColor,
|
||||
CurrentEyeColor,
|
||||
markingSet
|
||||
);
|
||||
for (var i = 0; i < colors.Count; i++)
|
||||
{
|
||||
markingObject.SetColor(i, colors[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Color everything in skin color
|
||||
for (var i = 0; i < marking.Sprites.Count; i++)
|
||||
{
|
||||
markingObject.SetColor(i, CurrentSkinColor);
|
||||
}
|
||||
}
|
||||
|
||||
markingObject.Forced = Forced;
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Content.Client.Preferences.UI
|
||||
private readonly IEntityManager _entMan;
|
||||
private readonly IConfigurationManager _configurationManager;
|
||||
private readonly MarkingManager _markingManager;
|
||||
|
||||
|
||||
private LineEdit _ageEdit => CAgeEdit;
|
||||
private LineEdit _nameEdit => CNameEdit;
|
||||
private LineEdit _flavorTextEdit = null!;
|
||||
@@ -223,6 +223,7 @@ namespace Content.Client.Preferences.UI
|
||||
return;
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithHairColor(newColor.marking.MarkingColors[0]));
|
||||
UpdateCMarkingsHair();
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
@@ -241,6 +242,7 @@ namespace Content.Client.Preferences.UI
|
||||
return;
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithFacialHairColor(newColor.marking.MarkingColors[0]));
|
||||
UpdateCMarkingsFacialHair();
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
@@ -252,6 +254,7 @@ namespace Content.Client.Preferences.UI
|
||||
Profile.Appearance.WithHairStyleName(HairStyles.DefaultHairStyle)
|
||||
);
|
||||
UpdateHairPickers();
|
||||
UpdateCMarkingsHair();
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
@@ -263,6 +266,7 @@ namespace Content.Client.Preferences.UI
|
||||
Profile.Appearance.WithFacialHairStyleName(HairStyles.DefaultFacialHairStyle)
|
||||
);
|
||||
UpdateHairPickers();
|
||||
UpdateCMarkingsFacialHair();
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
@@ -282,7 +286,7 @@ namespace Content.Client.Preferences.UI
|
||||
);
|
||||
|
||||
UpdateHairPickers();
|
||||
|
||||
UpdateCMarkingsHair();
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
@@ -302,7 +306,7 @@ namespace Content.Client.Preferences.UI
|
||||
);
|
||||
|
||||
UpdateHairPickers();
|
||||
|
||||
UpdateCMarkingsFacialHair();
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
@@ -343,6 +347,7 @@ namespace Content.Client.Preferences.UI
|
||||
return;
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithEyeColor(newColor));
|
||||
CMarkings.CurrentEyeColor = Profile.Appearance.EyeColor;
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
@@ -646,7 +651,7 @@ namespace Content.Client.Preferences.UI
|
||||
var color = SkinColor.HumanSkinTone((int) _skinColor.Value);
|
||||
|
||||
CMarkings.CurrentSkinColor = color;
|
||||
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));
|
||||
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));//
|
||||
break;
|
||||
}
|
||||
case HumanoidSkinColor.Hues:
|
||||
@@ -746,8 +751,8 @@ namespace Content.Client.Preferences.UI
|
||||
Profile = (HumanoidCharacterProfile) _preferencesManager.Preferences!.SelectedCharacter;
|
||||
CharacterSlot = _preferencesManager.Preferences.SelectedCharacterIndex;
|
||||
|
||||
_needUpdatePreview = true;
|
||||
UpdateControls();
|
||||
_needUpdatePreview = true;
|
||||
}
|
||||
|
||||
private void SetAge(int newAge)
|
||||
@@ -941,7 +946,9 @@ namespace Content.Client.Preferences.UI
|
||||
return;
|
||||
}
|
||||
|
||||
CMarkings.SetData(Profile.Appearance.Markings, Profile.Species, Profile.Appearance.SkinColor);
|
||||
CMarkings.SetData(Profile.Appearance.Markings, Profile.Species,
|
||||
Profile.Appearance.SkinColor, Profile.Appearance.EyeColor
|
||||
);
|
||||
}
|
||||
|
||||
private void UpdateSpecies()
|
||||
@@ -990,7 +997,6 @@ namespace Content.Client.Preferences.UI
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var hairMarking = Profile.Appearance.HairStyleId switch
|
||||
{
|
||||
HairStyles.DefaultHairStyle => new List<Marking>(),
|
||||
@@ -1013,6 +1019,76 @@ namespace Content.Client.Preferences.UI
|
||||
1);
|
||||
}
|
||||
|
||||
private void UpdateCMarkingsHair()
|
||||
{
|
||||
if (Profile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// hair color
|
||||
Color? hairColor = null;
|
||||
if ( Profile.Appearance.HairStyleId != HairStyles.DefaultHairStyle &&
|
||||
_markingManager.Markings.TryGetValue(Profile.Appearance.HairStyleId, out var hairProto)
|
||||
)
|
||||
{
|
||||
if (_markingManager.CanBeApplied(Profile.Species, hairProto, _prototypeManager))
|
||||
{
|
||||
if (_markingManager.MustMatchSkin(Profile.Species, HumanoidVisualLayers.Hair, _prototypeManager))
|
||||
{
|
||||
hairColor = Profile.Appearance.SkinColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
hairColor = Profile.Appearance.HairColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hairColor != null)
|
||||
{
|
||||
CMarkings.HairMarking = new (Profile.Appearance.HairStyleId, new List<Color>() { hairColor.Value });
|
||||
}
|
||||
else
|
||||
{
|
||||
CMarkings.HairMarking = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCMarkingsFacialHair()
|
||||
{
|
||||
if (Profile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// facial hair color
|
||||
Color? facialHairColor = null;
|
||||
if ( Profile.Appearance.FacialHairStyleId != HairStyles.DefaultFacialHairStyle &&
|
||||
_markingManager.Markings.TryGetValue(Profile.Appearance.FacialHairStyleId, out var facialHairProto)
|
||||
)
|
||||
{
|
||||
if (_markingManager.CanBeApplied(Profile.Species, facialHairProto, _prototypeManager))
|
||||
{
|
||||
if (_markingManager.MustMatchSkin(Profile.Species, HumanoidVisualLayers.Hair, _prototypeManager))
|
||||
{
|
||||
facialHairColor = Profile.Appearance.SkinColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
facialHairColor = Profile.Appearance.FacialHairColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (facialHairColor != null)
|
||||
{
|
||||
CMarkings.FacialHairMarking = new (Profile.Appearance.FacialHairStyleId, new List<Color>() { facialHairColor.Value });
|
||||
}
|
||||
else
|
||||
{
|
||||
CMarkings.FacialHairMarking = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateEyePickers()
|
||||
{
|
||||
if (Profile == null)
|
||||
@@ -1020,6 +1096,7 @@ namespace Content.Client.Preferences.UI
|
||||
return;
|
||||
}
|
||||
|
||||
CMarkings.CurrentEyeColor = Profile.Appearance.EyeColor;
|
||||
_eyesPicker.SetData(Profile.Appearance.EyeColor);
|
||||
}
|
||||
|
||||
@@ -1049,7 +1126,6 @@ namespace Content.Client.Preferences.UI
|
||||
UpdateClothingControls();
|
||||
UpdateBackpackControls();
|
||||
UpdateAgeEdit();
|
||||
UpdateHairPickers();
|
||||
UpdateEyePickers();
|
||||
UpdateSaveButton();
|
||||
UpdateJobPriorities();
|
||||
@@ -1057,6 +1133,9 @@ namespace Content.Client.Preferences.UI
|
||||
UpdateTraitPreferences();
|
||||
UpdateMarkings();
|
||||
RebuildSpriteView();
|
||||
UpdateHairPickers();
|
||||
UpdateCMarkingsHair();
|
||||
UpdateCMarkingsFacialHair();
|
||||
|
||||
_preferenceUnavailableButton.SelectId((int) Profile.PreferenceUnavailable);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user