Makes humanoid appearance component networked. (#13009)

Fixes https://github.com/space-wizards/space-station-14/issues/12248
This commit is contained in:
Leon Friedrich
2023-01-24 13:38:19 +13:00
committed by GitHub
parent 7ce8f7634a
commit 48bcd30ef9
50 changed files with 878 additions and 1074 deletions

View File

@@ -0,0 +1,351 @@
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Preferences;
using Robust.Client.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using static Content.Shared.Humanoid.HumanoidAppearanceState;
namespace Content.Client.Humanoid;
public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly MarkingManager _markingManager = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<HumanoidAppearanceComponent, ComponentHandleState>(OnHandleState);
}
private void OnHandleState(EntityUid uid, HumanoidAppearanceComponent component, ref ComponentHandleState args)
{
if (args.Current is not HumanoidAppearanceState state)
return;
ApplyState(uid, component, Comp<SpriteComponent>(uid), state);
}
private void ApplyState(EntityUid uid, HumanoidAppearanceComponent component, SpriteComponent sprite, HumanoidAppearanceState state)
{
component.Sex = state.Sex;
component.Species = state.Species;
component.Age = state.Age;
component.SkinColor = state.SkinColor;
component.EyeColor = state.EyeColor;
component.HiddenLayers = new(state.HiddenLayers);
component.PermanentlyHidden = new(state.PermanentlyHidden);
component.CustomBaseLayers = state.CustomBaseLayers.ShallowClone();
UpdateLayers(component, sprite);
ApplyMarkingSet(uid, state.Markings, component, sprite);
sprite[sprite.LayerMapReserveBlank(HumanoidVisualLayers.Eyes)].Color = state.EyeColor;
}
private static bool IsHidden(HumanoidAppearanceComponent humanoid, HumanoidVisualLayers layer)
=> humanoid.HiddenLayers.Contains(layer) || humanoid.PermanentlyHidden.Contains(layer);
private void UpdateLayers(HumanoidAppearanceComponent component, SpriteComponent sprite)
{
var oldLayers = new HashSet<HumanoidVisualLayers>(component.BaseLayers.Keys);
component.BaseLayers.Clear();
// add default species layers
var speciesProto = _prototypeManager.Index<SpeciesPrototype>(component.Species);
var baseSprites = _prototypeManager.Index<HumanoidSpeciesBaseSpritesPrototype>(speciesProto.SpriteSet);
foreach (var (key, id) in baseSprites.Sprites)
{
oldLayers.Remove(key);
if (!component.CustomBaseLayers.ContainsKey(key))
SetLayerData(component, sprite, key, id, sexMorph: true);
}
// add custom layers
foreach (var (key, info) in component.CustomBaseLayers)
{
oldLayers.Remove(key);
SetLayerData(component, sprite, key, info.ID, sexMorph: false, color: info.Color); ;
}
// hide old layers
// TODO maybe just remove them altogether?
foreach (var key in oldLayers)
{
if (sprite.LayerMapTryGet(key, out var index))
sprite[index].Visible = false;
}
}
private void SetLayerData(
HumanoidAppearanceComponent component,
SpriteComponent sprite,
HumanoidVisualLayers key,
string protoId,
bool sexMorph = false,
Color? color = null)
{
if (sexMorph)
protoId = HumanoidVisualLayersExtension.GetSexMorph(key, component.Sex, protoId);
var proto = _prototypeManager.Index<HumanoidSpeciesSpriteLayer>(protoId);
component.BaseLayers[key] = proto;
var layerIndex = sprite.LayerMapReserveBlank(key);
var layer = sprite[layerIndex];
if (color != null)
layer.Color = color.Value;
else if (proto.MatchSkin)
layer.Color = proto.MatchSkin ? component.SkinColor.WithAlpha(proto.LayerAlpha) : Color.White;
if (proto.BaseSprite != null)
sprite.LayerSetSprite(layerIndex, proto.BaseSprite);
layer.Visible = !IsHidden(component, key);
}
/// <summary>
/// Loads a profile directly into a humanoid.
/// </summary>
/// <param name="uid">The humanoid entity's UID</param>
/// <param name="profile">The profile to load.</param>
/// <param name="humanoid">The humanoid entity's humanoid component.</param>
/// <remarks>
/// This should not be used if the entity is owned by the server. The server will otherwise
/// override this with the appearance data it sends over.
/// </remarks>
public void LoadProfile(EntityUid uid, HumanoidCharacterProfile profile, HumanoidAppearanceComponent? humanoid = null)
{
if (!Resolve(uid, ref humanoid))
{
return;
}
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);
// 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);
var facialHair = new Marking(profile.Appearance.FacialHairStyleId,
new[] { profile.Appearance.FacialHairColor });
markings.AddBack(MarkingCategories.FacialHair, facialHair);
markings.FilterSpecies(profile.Species, _markingManager, _prototypeManager);
DebugTools.Assert(uid.IsClientSide());
var state = new HumanoidAppearanceState(markings,
new(),
new(),
customBaseLayers,
profile.Sex,
profile.Gender,
profile.Age,
profile.Species,
profile.Appearance.SkinColor,
profile.Appearance.EyeColor);
ApplyState(uid, humanoid, Comp<SpriteComponent>(uid), state);
}
private void ApplyMarkingSet(EntityUid uid,
MarkingSet newMarkings,
HumanoidAppearanceComponent humanoid,
SpriteComponent sprite)
{
// skip this entire thing if both sets are empty
if (humanoid.MarkingSet.Markings.Count == 0 && newMarkings.Markings.Count == 0)
return;
// I am lazy and I CBF resolving the previous mess, so I'm just going to nuke the markings.
// Really, markings should probably be a separate component altogether.
ClearAllMarkings(uid, humanoid, sprite);
humanoid.MarkingSet = new(newMarkings);
foreach (var markingList in humanoid.MarkingSet.Markings.Values)
{
foreach (var marking in markingList)
{
if (_markingManager.TryGetMarking(marking, out var markingPrototype))
ApplyMarking(uid, markingPrototype, marking.MarkingColors, marking.Visible, humanoid, sprite);
}
}
}
private void ClearAllMarkings(EntityUid uid, HumanoidAppearanceComponent humanoid,
SpriteComponent sprite)
{
foreach (var markingList in humanoid.MarkingSet.Markings.Values)
{
foreach (var marking in markingList)
{
RemoveMarking(uid, marking, sprite);
}
}
}
private void ClearMarkings(EntityUid uid, List<Marking> markings, HumanoidAppearanceComponent humanoid,
SpriteComponent spriteComp)
{
foreach (var marking in markings)
{
RemoveMarking(uid, marking, spriteComp);
}
}
private void RemoveMarking(EntityUid uid, Marking marking,
SpriteComponent spriteComp)
{
if (!_markingManager.TryGetMarking(marking, out var prototype))
{
return;
}
foreach (var sprite in prototype.Sprites)
{
if (sprite is not SpriteSpecifier.Rsi rsi)
{
continue;
}
var layerId = $"{marking.MarkingId}-{rsi.RsiState}";
if (!spriteComp.LayerMapTryGet(layerId, out var index))
{
continue;
}
spriteComp.LayerMapRemove(layerId);
spriteComp.RemoveLayer(index);
}
}
private void ApplyMarking(EntityUid uid,
MarkingPrototype markingPrototype,
IReadOnlyList<Color>? colors,
bool visible,
HumanoidAppearanceComponent humanoid,
SpriteComponent sprite)
{
if (!sprite.LayerMapTryGet(markingPrototype.BodyPart, out int targetLayer))
{
return;
}
visible &= !IsHidden(humanoid, markingPrototype.BodyPart);
visible &= humanoid.BaseLayers.TryGetValue(markingPrototype.BodyPart, out var setting)
&& setting.AllowsMarkings;
for (var j = 0; j < markingPrototype.Sprites.Count; j++)
{
if (markingPrototype.Sprites[j] is not SpriteSpecifier.Rsi rsi)
{
continue;
}
var layerId = $"{markingPrototype.ID}-{rsi.RsiState}";
if (!sprite.LayerMapTryGet(layerId, out _))
{
var layer = sprite.AddLayer(markingPrototype.Sprites[j], targetLayer + j + 1);
sprite.LayerMapSet(layerId, layer);
sprite.LayerSetSprite(layerId, rsi);
}
sprite.LayerSetVisible(layerId, visible);
if (!visible || setting == null) // this is kinda implied
{
continue;
}
if (markingPrototype.FollowSkinColor || colors == null || setting.MarkingsMatchSkin)
{
var skinColor = humanoid.SkinColor;
skinColor.A = setting.LayerAlpha;
sprite.LayerSetColor(layerId, skinColor);
}
else
{
sprite.LayerSetColor(layerId, colors[j]);
}
}
}
public override void SetSkinColor(EntityUid uid, Color skinColor, bool sync = true, HumanoidAppearanceComponent? humanoid = null)
{
if (!Resolve(uid, ref humanoid) || humanoid.SkinColor == skinColor)
return;
humanoid.SkinColor = skinColor;
if (sync)
Dirty(humanoid);
if (!TryComp(uid, out SpriteComponent? sprite))
return;
foreach (var (layer, spriteInfo) in humanoid.BaseLayers)
{
if (!spriteInfo.MatchSkin)
continue;
var index = sprite.LayerMapReserveBlank(layer);
sprite[index].Color = skinColor.WithAlpha(spriteInfo.LayerAlpha);
}
}
protected override void SetLayerVisibility(
EntityUid uid,
HumanoidAppearanceComponent humanoid,
HumanoidVisualLayers layer,
bool visible,
bool permanent,
ref bool dirty)
{
base.SetLayerVisibility(uid, humanoid, layer, visible, permanent, ref dirty);
var sprite = Comp<SpriteComponent>(uid);
if (!sprite.LayerMapTryGet(layer, out var index))
{
if (!visible)
return;
else
index = sprite.LayerMapReserveBlank(layer);
}
var spriteLayer = sprite[index];
if (spriteLayer.Visible == visible)
return;
spriteLayer.Visible = visible;
// I fucking hate this. I'll get around to refactoring sprite layers eventually I swear
foreach (var markingList in humanoid.MarkingSet.Markings.Values)
{
foreach (var marking in markingList)
{
if (_markingManager.TryGetMarking(marking, out var markingPrototype) && markingPrototype.BodyPart == layer)
ApplyMarking(uid, markingPrototype, marking.MarkingColors, marking.Visible, humanoid, sprite);
}
}
}
}

View File

@@ -1,6 +1,7 @@
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
using Robust.Client.GameObjects;
using static Content.Shared.Humanoid.HumanoidAppearanceState;
namespace Content.Client.Humanoid;

View File

@@ -4,6 +4,7 @@ using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using static Content.Shared.Humanoid.HumanoidAppearanceState;
namespace Content.Client.Humanoid;
@@ -63,7 +64,7 @@ public sealed partial class HumanoidMarkingModifierWindow : DefaultWindow
continue;
}
modifier.SetState(true, layerInfo.ID, layerInfo.Color);
modifier.SetState(true, layerInfo.ID, layerInfo.Color ?? Color.White);
}
}

View File

@@ -1,64 +0,0 @@
using System.Linq;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Preferences;
using Robust.Shared.Prototypes;
namespace Content.Client.Humanoid;
public sealed class HumanoidSystem : SharedHumanoidSystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly MarkingManager _markingManager = default!;
/// <summary>
/// Loads a profile directly into a humanoid.
/// </summary>
/// <param name="uid">The humanoid entity's UID</param>
/// <param name="profile">The profile to load.</param>
/// <param name="humanoid">The humanoid entity's humanoid component.</param>
/// <remarks>
/// This should not be used if the entity is owned by the server. The server will otherwise
/// override this with the appearance data it sends over.
/// </remarks>
public void LoadProfile(EntityUid uid, HumanoidCharacterProfile profile, HumanoidComponent? humanoid = null)
{
if (!Resolve(uid, ref humanoid))
{
return;
}
humanoid.Species = profile.Species;
var customBaseLayers = new Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo>
{
[HumanoidVisualLayers.Eyes] = new CustomBaseLayerInfo(string.Empty, profile.Appearance.EyeColor)
};
var speciesPrototype = _prototypeManager.Index<SpeciesPrototype>(profile.Species);
var markings = new MarkingSet(profile.Appearance.Markings, speciesPrototype.MarkingPoints, _markingManager,
_prototypeManager);
markings.EnsureDefault(profile.Appearance.SkinColor, _markingManager);
// 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);
var facialHair = new Marking(profile.Appearance.FacialHairStyleId,
new[] { profile.Appearance.FacialHairColor });
markings.AddBack(MarkingCategories.FacialHair, facialHair);
markings.FilterSpecies(profile.Species, _markingManager, _prototypeManager);
SetAppearance(uid,
profile.Species,
customBaseLayers,
profile.Appearance.SkinColor,
profile.Sex,
new(), // doesn't exist yet
markings.GetForwardEnumerator().ToList());
}
}

View File

@@ -1,447 +0,0 @@
using System.Linq;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
using Content.Shared.Humanoid.Prototypes;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.Humanoid;
public sealed class HumanoidVisualizerSystem : VisualizerSystem<HumanoidComponent>
{
[Dependency] private IPrototypeManager _prototypeManager = default!;
[Dependency] private MarkingManager _markingManager = default!;
protected override void OnAppearanceChange(EntityUid uid, HumanoidComponent component, ref AppearanceChangeEvent args)
{
base.OnAppearanceChange(uid, component, ref args);
if (args.Sprite == null)
{
return;
}
if (!args.AppearanceData.TryGetValue(HumanoidVisualizerKey.Key, out var dataRaw)
|| dataRaw is not HumanoidVisualizerData data)
{
return;
}
if (!_prototypeManager.TryIndex(data.Species, out SpeciesPrototype? speciesProto)
|| !_prototypeManager.TryIndex(speciesProto.SpriteSet, out HumanoidSpeciesBaseSpritesPrototype? baseSprites))
{
return;
}
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);
}
else
{
dirty |= MergeCustomBaseSprites(uid, baseSprites.Sprites, null, component);
}
if (dirty)
{
ApplyBaseSprites(uid, component, args.Sprite);
ApplySkinColor(uid, data.SkinColor, component, args.Sprite);
}
if (data.CustomBaseLayerInfo.Count != 0)
{
foreach (var (layer, info) in data.CustomBaseLayerInfo)
{
SetBaseLayerColor(uid, layer, info.Color, args.Sprite);
}
}
var layerVis = data.LayerVisibility.ToHashSet();
dirty |= ReplaceHiddenLayers(uid, layerVis, component, args.Sprite);
DiffAndApplyMarkings(uid, data.Markings, dirty, component, args.Sprite);
}
private bool ReplaceHiddenLayers(EntityUid uid, HashSet<HumanoidVisualLayers> hiddenLayers,
HumanoidComponent humanoid, SpriteComponent sprite)
{
if (hiddenLayers.SetEquals(humanoid.HiddenLayers))
{
return false;
}
SetSpriteVisibility(uid, hiddenLayers, false, sprite);
humanoid.HiddenLayers.ExceptWith(hiddenLayers);
SetSpriteVisibility(uid, humanoid.HiddenLayers, true, sprite);
humanoid.HiddenLayers.Clear();
humanoid.HiddenLayers.UnionWith(hiddenLayers);
return true;
}
private void SetSpriteVisibility(EntityUid uid, HashSet<HumanoidVisualLayers> layers, bool visibility, SpriteComponent sprite)
{
foreach (var layer in layers)
{
if (!sprite.LayerMapTryGet(layer, out var index))
{
continue;
}
sprite[index].Visible = visibility;
}
}
private void DiffAndApplyMarkings(EntityUid uid,
List<Marking> newMarkings,
bool layersDirty,
HumanoidComponent humanoid,
SpriteComponent sprite)
{
// skip this entire thing if both sets are empty
if (humanoid.CurrentClientMarkings.Count == 0 && newMarkings.Count == 0)
{
return;
}
var dirtyMarkings = new List<int>();
var dirtyRangeStart = humanoid.CurrentClientMarkings.Count == 0 ? 0 : -1;
// edge cases:
// humanoid.CurrentClientMarkings < newMarkings.Count
// - check if count matches this condition before diffing
// - if count is unequal, set dirty range to start from humanoid.CurrentClientMarkings.Count
// humanoid.CurrentClientMarkings > newMarkings.Count, no dirty markings
// - break count upon meeting this condition
// - clear markings from newMarkings.Count to humanoid.CurrentClientMarkings.Count - newMarkings.Count
for (var i = 0; i < humanoid.CurrentClientMarkings.Count; i++)
{
// if we've reached the end of the new set of markings,
// then that means it's time to finish
if (newMarkings.Count == i)
{
break;
}
// if the marking is different here, set the range start to i and break, we need
// to rebuild all markings starting from i
if (humanoid.CurrentClientMarkings[i].MarkingId != newMarkings[i].MarkingId)
{
dirtyRangeStart = i;
break;
}
// otherwise, we add the current marking to dirtyMarkings if it has different
// settings
// however: if the hidden layers are set to dirty, then we need to
// instead just add every single marking, since we don't know ahead of time
// where these markings go
if (humanoid.CurrentClientMarkings[i] != newMarkings[i] || layersDirty)
{
dirtyMarkings.Add(i);
}
}
foreach (var i in dirtyMarkings)
{
if (!_markingManager.TryGetMarking(newMarkings[i], out var dirtyMarking))
{
continue;
}
ApplyMarking(uid, dirtyMarking, newMarkings[i].MarkingColors, newMarkings[i].Visible, humanoid, sprite);
}
if (humanoid.CurrentClientMarkings.Count < newMarkings.Count && dirtyRangeStart < 0)
{
dirtyRangeStart = humanoid.CurrentClientMarkings.Count;
}
if (dirtyRangeStart >= 0)
{
var range = newMarkings.GetRange(dirtyRangeStart, newMarkings.Count - dirtyRangeStart);
if (humanoid.CurrentClientMarkings.Count > 0)
{
var oldRange = humanoid.CurrentClientMarkings.GetRange(dirtyRangeStart, humanoid.CurrentClientMarkings.Count - dirtyRangeStart);
ClearMarkings(uid, oldRange, humanoid, sprite);
}
ApplyMarkings(uid, range, humanoid, sprite);
}
else if (humanoid.CurrentClientMarkings.Count != newMarkings.Count)
{
if (newMarkings.Count == 0)
{
ClearAllMarkings(uid, humanoid, sprite);
}
else if (humanoid.CurrentClientMarkings.Count > newMarkings.Count)
{
var rangeStart = newMarkings.Count;
var rangeCount = humanoid.CurrentClientMarkings.Count - newMarkings.Count;
var range = humanoid.CurrentClientMarkings.GetRange(rangeStart, rangeCount);
ClearMarkings(uid, range, humanoid, sprite);
}
}
if (dirtyMarkings.Count > 0 || dirtyRangeStart >= 0 || humanoid.CurrentClientMarkings.Count != newMarkings.Count)
{
humanoid.CurrentClientMarkings = newMarkings;
}
}
private void ClearAllMarkings(EntityUid uid, HumanoidComponent humanoid,
SpriteComponent spriteComp)
{
ClearMarkings(uid, humanoid.CurrentClientMarkings, humanoid, spriteComp);
}
private void ClearMarkings(EntityUid uid, List<Marking> markings, HumanoidComponent humanoid,
SpriteComponent spriteComp)
{
foreach (var marking in markings)
{
RemoveMarking(uid, marking, spriteComp);
}
}
private void RemoveMarking(EntityUid uid, Marking marking,
SpriteComponent spriteComp)
{
if (!_markingManager.TryGetMarking(marking, out var prototype))
{
return;
}
foreach (var sprite in prototype.Sprites)
{
if (sprite is not SpriteSpecifier.Rsi rsi)
{
continue;
}
var layerId = $"{marking.MarkingId}-{rsi.RsiState}";
if (!spriteComp.LayerMapTryGet(layerId, out var index))
{
continue;
}
spriteComp.LayerMapRemove(layerId);
spriteComp.RemoveLayer(index);
}
}
private void ApplyMarkings(EntityUid uid,
List<Marking> markings,
HumanoidComponent humanoid,
SpriteComponent spriteComp)
{
foreach (var marking in new ReverseMarkingEnumerator(markings))
{
if (!_markingManager.TryGetMarking(marking, out var markingPrototype))
{
continue;
}
ApplyMarking(uid, markingPrototype, marking.MarkingColors, marking.Visible, humanoid, spriteComp);
}
}
private void ApplyMarking(EntityUid uid,
MarkingPrototype markingPrototype,
IReadOnlyList<Color>? colors,
bool visible,
HumanoidComponent humanoid,
SpriteComponent sprite)
{
if (!sprite.LayerMapTryGet(markingPrototype.BodyPart, out int targetLayer))
{
return;
}
visible &= !humanoid.HiddenLayers.Contains(markingPrototype.BodyPart);
visible &= humanoid.BaseLayers.TryGetValue(markingPrototype.BodyPart, out var setting)
&& setting.AllowsMarkings;
for (var j = 0; j < markingPrototype.Sprites.Count; j++)
{
if (markingPrototype.Sprites[j] is not SpriteSpecifier.Rsi rsi)
{
continue;
}
var layerId = $"{markingPrototype.ID}-{rsi.RsiState}";
if (!sprite.LayerMapTryGet(layerId, out _))
{
var layer = sprite.AddLayer(markingPrototype.Sprites[j], targetLayer + j + 1);
sprite.LayerMapSet(layerId, layer);
sprite.LayerSetSprite(layerId, rsi);
}
sprite.LayerSetVisible(layerId, visible);
if (!visible || setting == null) // this is kinda implied
{
continue;
}
if (markingPrototype.FollowSkinColor || colors == null || setting.MarkingsMatchSkin)
{
var skinColor = humanoid.SkinColor;
skinColor.A = setting.LayerAlpha;
sprite.LayerSetColor(layerId, skinColor);
}
else
{
sprite.LayerSetColor(layerId, colors[j]);
}
}
}
private void ApplySkinColor(EntityUid uid,
Color skinColor,
HumanoidComponent humanoid,
SpriteComponent spriteComp)
{
humanoid.SkinColor = skinColor;
foreach (var (layer, spriteInfo) in humanoid.BaseLayers)
{
if (!spriteInfo.MatchSkin)
{
continue;
}
var color = skinColor;
color.A = spriteInfo.LayerAlpha;
SetBaseLayerColor(uid, layer, color, spriteComp);
}
}
private void SetBaseLayerColor(EntityUid uid, HumanoidVisualLayers layer, Color color,
SpriteComponent sprite)
{
if (!sprite.LayerMapTryGet(layer, out var index))
{
return;
}
sprite[index].Color = color;
}
private bool MergeCustomBaseSprites(EntityUid uid, Dictionary<HumanoidVisualLayers, string> baseSprites,
Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo>? customBaseSprites,
HumanoidComponent humanoid)
{
var newBaseLayers = new Dictionary<HumanoidVisualLayers, HumanoidSpeciesSpriteLayer>();
foreach (var (key, id) in baseSprites)
{
var sexMorph = humanoid.Sex switch
{
Sex.Male when HumanoidVisualLayersExtension.HasSexMorph(key) => $"{id}Male",
Sex.Female when HumanoidVisualLayersExtension.HasSexMorph(key) => $"{id}Female",
_ => id
};
if (!_prototypeManager.TryIndex(sexMorph, out HumanoidSpeciesSpriteLayer? baseLayer))
{
continue;
}
if (!newBaseLayers.TryAdd(key, baseLayer))
{
newBaseLayers[key] = baseLayer;
}
}
if (customBaseSprites == null)
{
return IsDirty(newBaseLayers);
}
foreach (var (key, info) in customBaseSprites)
{
if (!_prototypeManager.TryIndex(info.ID, out HumanoidSpeciesSpriteLayer? baseLayer))
{
continue;
}
if (!newBaseLayers.TryAdd(key, baseLayer))
{
newBaseLayers[key] = baseLayer;
}
}
bool IsDirty(Dictionary<HumanoidVisualLayers, HumanoidSpeciesSpriteLayer> newBaseLayers)
{
var dirty = false;
if (humanoid.BaseLayers.Count != newBaseLayers.Count)
{
dirty = true;
humanoid.BaseLayers = newBaseLayers;
return dirty;
}
foreach (var (key, info) in humanoid.BaseLayers)
{
if (!newBaseLayers.TryGetValue(key, out var newInfo))
{
dirty = true;
break;
}
if (info.ID != newInfo.ID)
{
dirty = true;
break;
}
}
if (dirty)
{
humanoid.BaseLayers = newBaseLayers;
}
return dirty;
}
return IsDirty(newBaseLayers);
}
private void ApplyBaseSprites(EntityUid uid,
HumanoidComponent humanoid,
SpriteComponent spriteComp)
{
foreach (var (layer, spriteInfo) in humanoid.BaseLayers)
{
if (spriteInfo.BaseSprite != null && spriteComp.LayerMapTryGet(layer, out var index))
{
switch (spriteInfo.BaseSprite)
{
case SpriteSpecifier.Rsi rsi:
spriteComp.LayerSetRSI(index, rsi.RsiPath);
spriteComp.LayerSetState(index, rsi.RsiState);
break;
case SpriteSpecifier.Texture texture:
spriteComp.LayerSetTexture(index, texture.TexturePath);
break;
}
}
}
}
}

View File

@@ -34,7 +34,7 @@ public sealed partial class MarkingPicker : Control
private List<MarkingCategories> _markingCategories = Enum.GetValues<MarkingCategories>().ToList();
private string _currentSpecies = SharedHumanoidSystem.DefaultSpecies;
private string _currentSpecies = SharedHumanoidAppearanceSystem.DefaultSpecies;
public Color CurrentSkinColor = Color.White;
private readonly HashSet<MarkingCategories> _ignoreCategories = new();
@@ -361,12 +361,13 @@ public sealed partial class MarkingPicker : Control
colorContainer.AddChild(new Label { Text = $"{stateNames[i]} color:" });
colorContainer.AddChild(colorSelector);
var listing = _currentMarkings[_selectedMarkingCategory];
var listing = _currentMarkings.Markings[_selectedMarkingCategory];
var color = listing[listing.Count - 1 - item.ItemIndex].MarkingColors[i];
var currentColor = new Color(
listing[listing.Count - 1 - item.ItemIndex].MarkingColors[i].RByte,
listing[listing.Count - 1 - item.ItemIndex].MarkingColors[i].GByte,
listing[listing.Count - 1 - item.ItemIndex].MarkingColors[i].BByte
color.RByte,
color.GByte,
color.BByte
);
colorSelector.Color = currentColor;
_currentMarkingColors.Add(currentColor);
@@ -394,7 +395,7 @@ public sealed partial class MarkingPicker : Control
_selectedMarking.IconModulate = _currentMarkingColors[colorIndex];
var marking = new Marking(_currentMarkings[_selectedMarkingCategory][markingIndex]);
var marking = new Marking(_currentMarkings.Markings[_selectedMarkingCategory][markingIndex]);
marking.SetColor(colorIndex, _currentMarkingColors[colorIndex]);
_currentMarkings.Replace(_selectedMarkingCategory, markingIndex, marking);