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:
csqrb
2023-03-05 08:59:07 +06:00
committed by GitHub
parent 0ad9af7ae2
commit 8b3d7728d7
26 changed files with 863 additions and 83 deletions

View File

@@ -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);
}
}
}

View File

@@ -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

View File

@@ -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;