Makes humanoid appearance component networked. (#13009)
Fixes https://github.com/space-wizards/space-station-14/issues/12248
This commit is contained in:
@@ -65,9 +65,8 @@ public sealed class ClientClothingSystem : ClothingSystem
|
|||||||
if (!TryComp(uid, out SpriteComponent? sprite) || !sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out var layer))
|
if (!TryComp(uid, out SpriteComponent? sprite) || !sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out var layer))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!args.AppearanceData.TryGetValue(HumanoidVisualizerKey.Key, out object? obj)
|
if (!TryComp(uid, out HumanoidAppearanceComponent? humanoid)
|
||||||
|| obj is not HumanoidVisualizerData data
|
|| humanoid.Sex != Sex.Female
|
||||||
|| data.Sex != Sex.Female
|
|
||||||
|| !_inventorySystem.TryGetSlotEntity(uid, "jumpsuit", out var suit, component)
|
|| !_inventorySystem.TryGetSlotEntity(uid, "jumpsuit", out var suit, component)
|
||||||
|| !TryComp(suit, out ClothingComponent? clothing))
|
|| !TryComp(suit, out ClothingComponent? clothing))
|
||||||
{
|
{
|
||||||
@@ -219,8 +218,7 @@ public sealed class ClientClothingSystem : ClothingSystem
|
|||||||
|
|
||||||
if (slot == "jumpsuit" && sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out var suitLayer))
|
if (slot == "jumpsuit" && sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out var suitLayer))
|
||||||
{
|
{
|
||||||
if (_appearance.TryGetData<HumanoidVisualizerData>(equipee, HumanoidVisualizerKey.Key, out var data)
|
if (TryComp(equipee, out HumanoidAppearanceComponent? humanoid) && humanoid.Sex == Sex.Female)
|
||||||
&& data.Sex == Sex.Female)
|
|
||||||
{
|
{
|
||||||
sprite.LayerSetState(suitLayer, clothingComponent.FemaleMask switch
|
sprite.LayerSetState(suitLayer, clothingComponent.FemaleMask switch
|
||||||
{
|
{
|
||||||
|
|||||||
351
Content.Client/Humanoid/HumanoidAppearanceSystem.cs
Normal file
351
Content.Client/Humanoid/HumanoidAppearanceSystem.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using Content.Shared.Humanoid;
|
using Content.Shared.Humanoid;
|
||||||
using Content.Shared.Humanoid.Markings;
|
using Content.Shared.Humanoid.Markings;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
|
using static Content.Shared.Humanoid.HumanoidAppearanceState;
|
||||||
|
|
||||||
namespace Content.Client.Humanoid;
|
namespace Content.Client.Humanoid;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Robust.Client.AutoGenerated;
|
|||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using static Content.Shared.Humanoid.HumanoidAppearanceState;
|
||||||
|
|
||||||
namespace Content.Client.Humanoid;
|
namespace Content.Client.Humanoid;
|
||||||
|
|
||||||
@@ -63,7 +64,7 @@ public sealed partial class HumanoidMarkingModifierWindow : DefaultWindow
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
modifier.SetState(true, layerInfo.ID, layerInfo.Color);
|
modifier.SetState(true, layerInfo.ID, layerInfo.Color ?? Color.White);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -34,7 +34,7 @@ public sealed partial class MarkingPicker : Control
|
|||||||
|
|
||||||
private List<MarkingCategories> _markingCategories = Enum.GetValues<MarkingCategories>().ToList();
|
private List<MarkingCategories> _markingCategories = Enum.GetValues<MarkingCategories>().ToList();
|
||||||
|
|
||||||
private string _currentSpecies = SharedHumanoidSystem.DefaultSpecies;
|
private string _currentSpecies = SharedHumanoidAppearanceSystem.DefaultSpecies;
|
||||||
public Color CurrentSkinColor = Color.White;
|
public Color CurrentSkinColor = Color.White;
|
||||||
|
|
||||||
private readonly HashSet<MarkingCategories> _ignoreCategories = new();
|
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(new Label { Text = $"{stateNames[i]} color:" });
|
||||||
colorContainer.AddChild(colorSelector);
|
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(
|
var currentColor = new Color(
|
||||||
listing[listing.Count - 1 - item.ItemIndex].MarkingColors[i].RByte,
|
color.RByte,
|
||||||
listing[listing.Count - 1 - item.ItemIndex].MarkingColors[i].GByte,
|
color.GByte,
|
||||||
listing[listing.Count - 1 - item.ItemIndex].MarkingColors[i].BByte
|
color.BByte
|
||||||
);
|
);
|
||||||
colorSelector.Color = currentColor;
|
colorSelector.Color = currentColor;
|
||||||
_currentMarkingColors.Add(currentColor);
|
_currentMarkingColors.Add(currentColor);
|
||||||
@@ -394,7 +395,7 @@ public sealed partial class MarkingPicker : Control
|
|||||||
|
|
||||||
_selectedMarking.IconModulate = _currentMarkingColors[colorIndex];
|
_selectedMarking.IconModulate = _currentMarkingColors[colorIndex];
|
||||||
|
|
||||||
var marking = new Marking(_currentMarkings[_selectedMarkingCategory][markingIndex]);
|
var marking = new Marking(_currentMarkings.Markings[_selectedMarkingCategory][markingIndex]);
|
||||||
marking.SetColor(colorIndex, _currentMarkingColors[colorIndex]);
|
marking.SetColor(colorIndex, _currentMarkingColors[colorIndex]);
|
||||||
_currentMarkings.Replace(_selectedMarkingCategory, markingIndex, marking);
|
_currentMarkings.Replace(_selectedMarkingCategory, markingIndex, marking);
|
||||||
|
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ namespace Content.Client.Lobby.UI
|
|||||||
_viewBox.AddChild(viewWest);
|
_viewBox.AddChild(viewWest);
|
||||||
_viewBox.AddChild(viewEast);
|
_viewBox.AddChild(viewEast);
|
||||||
_summaryLabel.Text = selectedCharacter.Summary;
|
_summaryLabel.Text = selectedCharacter.Summary;
|
||||||
EntitySystem.Get<HumanoidSystem>().LoadProfile(_previewDummy.Value, selectedCharacter);
|
EntitySystem.Get<HumanoidAppearanceSystem>().LoadProfile(_previewDummy.Value, selectedCharacter);
|
||||||
GiveDummyJobClothes(_previewDummy.Value, selectedCharacter);
|
GiveDummyJobClothes(_previewDummy.Value, selectedCharacter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,10 +168,10 @@ namespace Content.Client.Preferences.UI
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_previewDummy = entityManager.SpawnEntity(prototypeManager.Index<SpeciesPrototype>(SharedHumanoidSystem.DefaultSpecies).DollPrototype, MapCoordinates.Nullspace);
|
_previewDummy = entityManager.SpawnEntity(prototypeManager.Index<SpeciesPrototype>(SharedHumanoidAppearanceSystem.DefaultSpecies).DollPrototype, MapCoordinates.Nullspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
EntitySystem.Get<HumanoidSystem>().LoadProfile(_previewDummy, (HumanoidCharacterProfile)profile);
|
EntitySystem.Get<HumanoidAppearanceSystem>().LoadProfile(_previewDummy, (HumanoidCharacterProfile)profile);
|
||||||
|
|
||||||
if (humanoid != null)
|
if (humanoid != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -553,7 +553,7 @@ namespace Content.Client.Preferences.UI
|
|||||||
#endregion FlavorText
|
#endregion FlavorText
|
||||||
|
|
||||||
#region Dummy
|
#region Dummy
|
||||||
var species = Profile?.Species ?? SharedHumanoidSystem.DefaultSpecies;
|
var species = Profile?.Species ?? SharedHumanoidAppearanceSystem.DefaultSpecies;
|
||||||
var dollProto = _prototypeManager.Index<SpeciesPrototype>(species).DollPrototype;
|
var dollProto = _prototypeManager.Index<SpeciesPrototype>(species).DollPrototype;
|
||||||
|
|
||||||
if (_previewDummy != null)
|
if (_previewDummy != null)
|
||||||
@@ -693,7 +693,7 @@ namespace Content.Client.Preferences.UI
|
|||||||
|
|
||||||
private void RebuildSpriteView()
|
private void RebuildSpriteView()
|
||||||
{
|
{
|
||||||
var species = Profile?.Species ?? SharedHumanoidSystem.DefaultSpecies;
|
var species = Profile?.Species ?? SharedHumanoidAppearanceSystem.DefaultSpecies;
|
||||||
var dollProto = _prototypeManager.Index<SpeciesPrototype>(species).DollPrototype;
|
var dollProto = _prototypeManager.Index<SpeciesPrototype>(species).DollPrototype;
|
||||||
|
|
||||||
if (_previewDummy != null)
|
if (_previewDummy != null)
|
||||||
@@ -1032,7 +1032,7 @@ namespace Content.Client.Preferences.UI
|
|||||||
if (Profile is null)
|
if (Profile is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
EntitySystem.Get<HumanoidSystem>().LoadProfile(_previewDummy!.Value, Profile);
|
EntitySystem.Get<HumanoidAppearanceSystem>().LoadProfile(_previewDummy!.Value, Profile);
|
||||||
LobbyCharacterPreviewPanel.GiveDummyJobClothes(_previewDummy!.Value, Profile);
|
LobbyCharacterPreviewPanel.GiveDummyJobClothes(_previewDummy!.Value, Profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public sealed class BodySystem : SharedBodySystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly GameTicker _ticker = default!;
|
[Dependency] private readonly GameTicker _ticker = default!;
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
[Dependency] private readonly HumanoidSystem _humanoidSystem = default!;
|
[Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!;
|
||||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ public sealed class BodySystem : SharedBodySystem
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (part.Body is { } body &&
|
if (part.Body is { } body &&
|
||||||
TryComp<HumanoidComponent>(body, out var humanoid))
|
TryComp<HumanoidAppearanceComponent>(body, out var humanoid))
|
||||||
{
|
{
|
||||||
var layer = part.ToHumanoidLayers();
|
var layer = part.ToHumanoidLayers();
|
||||||
if (layer != null)
|
if (layer != null)
|
||||||
@@ -103,7 +103,7 @@ public sealed class BodySystem : SharedBodySystem
|
|||||||
if (!base.DropPart(partId, part))
|
if (!base.DropPart(partId, part))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (oldBody == null || !TryComp<HumanoidComponent>(oldBody, out var humanoid))
|
if (oldBody == null || !TryComp<HumanoidAppearanceComponent>(oldBody, out var humanoid))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var layer = part.ToHumanoidLayers();
|
var layer = part.ToHumanoidLayers();
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ using Robust.Shared.Random;
|
|||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
|
using Content.Shared.Humanoid;
|
||||||
|
|
||||||
namespace Content.Server.Cloning
|
namespace Content.Server.Cloning
|
||||||
{
|
{
|
||||||
@@ -41,7 +42,7 @@ namespace Content.Server.Cloning
|
|||||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||||
[Dependency] private readonly EuiManager _euiManager = null!;
|
[Dependency] private readonly EuiManager _euiManager = null!;
|
||||||
[Dependency] private readonly CloningConsoleSystem _cloningConsoleSystem = default!;
|
[Dependency] private readonly CloningConsoleSystem _cloningConsoleSystem = default!;
|
||||||
[Dependency] private readonly HumanoidSystem _humanoidSystem = default!;
|
[Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!;
|
||||||
[Dependency] private readonly ContainerSystem _containerSystem = default!;
|
[Dependency] private readonly ContainerSystem _containerSystem = default!;
|
||||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||||
[Dependency] private readonly PowerReceiverSystem _powerReceiverSystem = default!;
|
[Dependency] private readonly PowerReceiverSystem _powerReceiverSystem = default!;
|
||||||
@@ -170,7 +171,7 @@ namespace Content.Server.Cloning
|
|||||||
if (mind.UserId == null || !_playerManager.TryGetSessionById(mind.UserId.Value, out var client))
|
if (mind.UserId == null || !_playerManager.TryGetSessionById(mind.UserId.Value, out var client))
|
||||||
return false; // If we can't track down the client, we can't offer transfer. That'd be quite bad.
|
return false; // If we can't track down the client, we can't offer transfer. That'd be quite bad.
|
||||||
|
|
||||||
if (!TryComp<HumanoidComponent>(bodyToClone, out var humanoid))
|
if (!TryComp<HumanoidAppearanceComponent>(bodyToClone, out var humanoid))
|
||||||
return false; // whatever body was to be cloned, was not a humanoid
|
return false; // whatever body was to be cloned, was not a humanoid
|
||||||
|
|
||||||
if (!_prototype.TryIndex<SpeciesPrototype>(humanoid.Species, out var speciesPrototype))
|
if (!_prototype.TryIndex<SpeciesPrototype>(humanoid.Species, out var speciesPrototype))
|
||||||
|
|||||||
@@ -1,39 +1,7 @@
|
|||||||
using Content.Server.Humanoid;
|
|
||||||
using Content.Shared.Clothing.Components;
|
|
||||||
using Content.Shared.Clothing.EntitySystems;
|
using Content.Shared.Clothing.EntitySystems;
|
||||||
using Content.Shared.Humanoid;
|
|
||||||
using Content.Shared.Inventory.Events;
|
|
||||||
using Content.Shared.Tag;
|
|
||||||
|
|
||||||
namespace Content.Server.Clothing;
|
namespace Content.Server.Clothing;
|
||||||
|
|
||||||
public sealed class ServerClothingSystem : ClothingSystem
|
public sealed class ServerClothingSystem : ClothingSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly HumanoidSystem _humanoidSystem = default!;
|
|
||||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
|
||||||
|
|
||||||
protected override void OnGotEquipped(EntityUid uid, ClothingComponent component, GotEquippedEvent args)
|
|
||||||
{
|
|
||||||
base.OnGotEquipped(uid, component, args);
|
|
||||||
// why the fuck is humanoid visuals server-only???
|
|
||||||
|
|
||||||
if (args.Slot == "head"
|
|
||||||
&& _tagSystem.HasTag(args.Equipment, "HidesHair"))
|
|
||||||
{
|
|
||||||
_humanoidSystem.ToggleHiddenLayer(args.Equipee, HumanoidVisualLayers.Hair);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnGotUnequipped(EntityUid uid, ClothingComponent component, GotUnequippedEvent args)
|
|
||||||
{
|
|
||||||
base.OnGotUnequipped(uid, component, args);
|
|
||||||
|
|
||||||
// why the fuck is humanoid visuals server-only???
|
|
||||||
|
|
||||||
if (args.Slot == "head"
|
|
||||||
&& _tagSystem.HasTag(args.Equipment, "HidesHair"))
|
|
||||||
{
|
|
||||||
_humanoidSystem.ToggleHiddenLayer(args.Equipee, HumanoidVisualLayers.Hair);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ namespace Content.Server.Dragon
|
|||||||
var ichorInjection = new Solution(component.DevourChem, component.DevourHealRate);
|
var ichorInjection = new Solution(component.DevourChem, component.DevourHealRate);
|
||||||
|
|
||||||
//Humanoid devours allow dragon to get eggs, corpses included
|
//Humanoid devours allow dragon to get eggs, corpses included
|
||||||
if (!EntityManager.HasComponent<HumanoidComponent>(args.Target))
|
if (!EntityManager.HasComponent<HumanoidAppearanceComponent>(args.Target))
|
||||||
{
|
{
|
||||||
ichorInjection.ScaleSolution(0.5f);
|
ichorInjection.ScaleSolution(0.5f);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ public sealed class ZombieRuleSystem : GameRuleSystem
|
|||||||
private void CheckRoundEnd(EntityUid target)
|
private void CheckRoundEnd(EntityUid target)
|
||||||
{
|
{
|
||||||
//we only care about players, not monkeys and such.
|
//we only care about players, not monkeys and such.
|
||||||
if (!HasComp<HumanoidComponent>(target))
|
if (!HasComp<HumanoidAppearanceComponent>(target))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var percent = GetInfectedPercentage(out var num);
|
var percent = GetInfectedPercentage(out var num);
|
||||||
@@ -196,7 +196,7 @@ public sealed class ZombieRuleSystem : GameRuleSystem
|
|||||||
|
|
||||||
private float GetInfectedPercentage(out List<EntityUid> livingHumans)
|
private float GetInfectedPercentage(out List<EntityUid> livingHumans)
|
||||||
{
|
{
|
||||||
var allPlayers = EntityQuery<HumanoidComponent, MobStateComponent>(true);
|
var allPlayers = EntityQuery<HumanoidAppearanceComponent, MobStateComponent>(true);
|
||||||
var allZombers = GetEntityQuery<ZombieComponent>();
|
var allZombers = GetEntityQuery<ZombieComponent>();
|
||||||
|
|
||||||
var totalPlayers = new List<EntityUid>();
|
var totalPlayers = new List<EntityUid>();
|
||||||
|
|||||||
@@ -1,50 +1,32 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Server.GameTicking;
|
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Humanoid;
|
using Content.Shared.Humanoid;
|
||||||
using Content.Shared.Humanoid.Markings;
|
using Content.Shared.Humanoid.Markings;
|
||||||
using Content.Shared.Humanoid.Prototypes;
|
using Content.Shared.Humanoid.Prototypes;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Inventory.Events;
|
|
||||||
using Content.Shared.Preferences;
|
using Content.Shared.Preferences;
|
||||||
using Content.Shared.Tag;
|
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
using Robust.Shared.GameObjects.Components.Localization;
|
using Robust.Shared.GameObjects.Components.Localization;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.Humanoid;
|
namespace Content.Server.Humanoid;
|
||||||
|
|
||||||
public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
public sealed partial class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly MarkingManager _markingManager = default!;
|
[Dependency] private readonly MarkingManager _markingManager = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
SubscribeLocalEvent<HumanoidComponent, ComponentInit>(OnInit);
|
base.Initialize();
|
||||||
SubscribeLocalEvent<HumanoidComponent, HumanoidMarkingModifierMarkingSetMessage>(OnMarkingsSet);
|
SubscribeLocalEvent<HumanoidAppearanceComponent, ComponentInit>(OnInit);
|
||||||
SubscribeLocalEvent<HumanoidComponent, HumanoidMarkingModifierBaseLayersSetMessage>(OnBaseLayersSet);
|
SubscribeLocalEvent<HumanoidAppearanceComponent, HumanoidMarkingModifierMarkingSetMessage>(OnMarkingsSet);
|
||||||
SubscribeLocalEvent<HumanoidComponent, GetVerbsEvent<Verb>>(OnVerbsRequest);
|
SubscribeLocalEvent<HumanoidAppearanceComponent, HumanoidMarkingModifierBaseLayersSetMessage>(OnBaseLayersSet);
|
||||||
SubscribeLocalEvent<HumanoidComponent, ExaminedEvent>(OnExamined);
|
SubscribeLocalEvent<HumanoidAppearanceComponent, GetVerbsEvent<Verb>>(OnVerbsRequest);
|
||||||
|
SubscribeLocalEvent<HumanoidAppearanceComponent, ExaminedEvent>(OnExamined);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Synchronize(EntityUid uid, HumanoidComponent? component = null)
|
private void OnInit(EntityUid uid, HumanoidAppearanceComponent humanoid, ComponentInit args)
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref component))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetAppearance(uid,
|
|
||||||
component.Species,
|
|
||||||
component.CustomBaseLayers,
|
|
||||||
component.SkinColor,
|
|
||||||
component.Sex,
|
|
||||||
component.AllHiddenLayers.ToList(),
|
|
||||||
component.CurrentMarkings.GetForwardEnumerator().ToList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnInit(EntityUid uid, HumanoidComponent humanoid, ComponentInit args)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(humanoid.Species))
|
if (string.IsNullOrEmpty(humanoid.Species))
|
||||||
{
|
{
|
||||||
@@ -67,7 +49,7 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
LoadProfile(uid, startingSet.Profile, humanoid);
|
LoadProfile(uid, startingSet.Profile, humanoid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnExamined(EntityUid uid, HumanoidComponent component, ExaminedEvent args)
|
private void OnExamined(EntityUid uid, HumanoidAppearanceComponent component, ExaminedEvent args)
|
||||||
{
|
{
|
||||||
var identity = Identity.Entity(component.Owner, EntityManager);
|
var identity = Identity.Entity(component.Owner, EntityManager);
|
||||||
var species = GetSpeciesRepresentation(component.Species).ToLower();
|
var species = GetSpeciesRepresentation(component.Species).ToLower();
|
||||||
@@ -82,7 +64,7 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
/// <param name="uid">The mob's entity UID.</param>
|
/// <param name="uid">The mob's entity UID.</param>
|
||||||
/// <param name="profile">The character profile to load.</param>
|
/// <param name="profile">The character profile to load.</param>
|
||||||
/// <param name="humanoid">Humanoid component of the entity</param>
|
/// <param name="humanoid">Humanoid component of the entity</param>
|
||||||
public void LoadProfile(EntityUid uid, HumanoidCharacterProfile profile, HumanoidComponent? humanoid = null)
|
public void LoadProfile(EntityUid uid, HumanoidCharacterProfile profile, HumanoidAppearanceComponent? humanoid = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref humanoid))
|
if (!Resolve(uid, ref humanoid))
|
||||||
{
|
{
|
||||||
@@ -91,11 +73,11 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
|
|
||||||
SetSpecies(uid, profile.Species, false, humanoid);
|
SetSpecies(uid, profile.Species, false, humanoid);
|
||||||
humanoid.Sex = profile.Sex;
|
humanoid.Sex = profile.Sex;
|
||||||
|
humanoid.EyeColor = profile.Appearance.EyeColor;
|
||||||
|
|
||||||
SetSkinColor(uid, profile.Appearance.SkinColor, false);
|
SetSkinColor(uid, profile.Appearance.SkinColor, false);
|
||||||
SetBaseLayerColor(uid, HumanoidVisualLayers.Eyes, profile.Appearance.EyeColor, false);
|
|
||||||
|
|
||||||
humanoid.CurrentMarkings.Clear();
|
humanoid.MarkingSet.Clear();
|
||||||
|
|
||||||
// Hair/facial hair - this may eventually be deprecated.
|
// Hair/facial hair - this may eventually be deprecated.
|
||||||
|
|
||||||
@@ -117,7 +99,7 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
|
|
||||||
humanoid.Age = profile.Age;
|
humanoid.Age = profile.Age;
|
||||||
|
|
||||||
Synchronize(uid);
|
Dirty(humanoid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this was done enough times that it only made sense to do it here
|
// this was done enough times that it only made sense to do it here
|
||||||
@@ -129,8 +111,8 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
/// <param name="target">Target entity to apply the source entity's appearance to.</param>
|
/// <param name="target">Target entity to apply the source entity's appearance to.</param>
|
||||||
/// <param name="sourceHumanoid">Source entity's humanoid component.</param>
|
/// <param name="sourceHumanoid">Source entity's humanoid component.</param>
|
||||||
/// <param name="targetHumanoid">Target entity's humanoid component.</param>
|
/// <param name="targetHumanoid">Target entity's humanoid component.</param>
|
||||||
public void CloneAppearance(EntityUid source, EntityUid target, HumanoidComponent? sourceHumanoid = null,
|
public void CloneAppearance(EntityUid source, EntityUid target, HumanoidAppearanceComponent? sourceHumanoid = null,
|
||||||
HumanoidComponent? targetHumanoid = null)
|
HumanoidAppearanceComponent? targetHumanoid = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(source, ref sourceHumanoid) || !Resolve(target, ref targetHumanoid))
|
if (!Resolve(source, ref sourceHumanoid) || !Resolve(target, ref targetHumanoid))
|
||||||
{
|
{
|
||||||
@@ -141,7 +123,7 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
targetHumanoid.SkinColor = sourceHumanoid.SkinColor;
|
targetHumanoid.SkinColor = sourceHumanoid.SkinColor;
|
||||||
targetHumanoid.Sex = sourceHumanoid.Sex;
|
targetHumanoid.Sex = sourceHumanoid.Sex;
|
||||||
targetHumanoid.CustomBaseLayers = new(sourceHumanoid.CustomBaseLayers);
|
targetHumanoid.CustomBaseLayers = new(sourceHumanoid.CustomBaseLayers);
|
||||||
targetHumanoid.CurrentMarkings = new(sourceHumanoid.CurrentMarkings);
|
targetHumanoid.MarkingSet = new(sourceHumanoid.MarkingSet);
|
||||||
|
|
||||||
targetHumanoid.Gender = sourceHumanoid.Gender;
|
targetHumanoid.Gender = sourceHumanoid.Gender;
|
||||||
if (TryComp<GrammarComponent>(target, out var grammar))
|
if (TryComp<GrammarComponent>(target, out var grammar))
|
||||||
@@ -149,180 +131,7 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
grammar.Gender = sourceHumanoid.Gender;
|
grammar.Gender = sourceHumanoid.Gender;
|
||||||
}
|
}
|
||||||
|
|
||||||
Synchronize(target, targetHumanoid);
|
Dirty(targetHumanoid);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set a humanoid mob's species. This will change their base sprites, as well as their current
|
|
||||||
/// set of markings to fit against the mob's new species.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uid">The humanoid mob's UID.</param>
|
|
||||||
/// <param name="species">The species to set the mob to. Will return if the species prototype was invalid.</param>
|
|
||||||
/// <param name="sync">Whether to immediately synchronize this to the humanoid mob, or not.</param>
|
|
||||||
/// <param name="humanoid">Humanoid component of the entity</param>
|
|
||||||
public void SetSpecies(EntityUid uid, string species, bool sync = true, HumanoidComponent? humanoid = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref humanoid) || !_prototypeManager.TryIndex<SpeciesPrototype>(species, out var prototype))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
humanoid.Species = species;
|
|
||||||
humanoid.CurrentMarkings.FilterSpecies(species, _markingManager);
|
|
||||||
var oldMarkings = humanoid.CurrentMarkings.GetForwardEnumerator().ToList();
|
|
||||||
humanoid.CurrentMarkings = new(oldMarkings, prototype.MarkingPoints, _markingManager, _prototypeManager);
|
|
||||||
|
|
||||||
if (sync)
|
|
||||||
{
|
|
||||||
Synchronize(uid, humanoid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the skin color of this humanoid mob. Will only affect base layers that are not custom,
|
|
||||||
/// custom base layers should use <see cref="SetBaseLayerColor"/> instead.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uid">The humanoid mob's UID.</param>
|
|
||||||
/// <param name="skinColor">Skin color to set on the humanoid mob.</param>
|
|
||||||
/// <param name="sync">Whether to synchronize this to the humanoid mob, or not.</param>
|
|
||||||
/// <param name="humanoid">Humanoid component of the entity</param>
|
|
||||||
public void SetSkinColor(EntityUid uid, Color skinColor, bool sync = true, HumanoidComponent? humanoid = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref humanoid))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
humanoid.SkinColor = skinColor;
|
|
||||||
|
|
||||||
if (sync)
|
|
||||||
Synchronize(uid, humanoid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the base layer ID of this humanoid mob. A humanoid mob's 'base layer' is
|
|
||||||
/// the skin sprite that is applied to the mob's sprite upon appearance refresh.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uid">The humanoid mob's UID.</param>
|
|
||||||
/// <param name="layer">The layer to target on this humanoid mob.</param>
|
|
||||||
/// <param name="id">The ID of the sprite to use. See <see cref="HumanoidSpeciesSpriteLayer"/>.</param>
|
|
||||||
/// <param name="sync">Whether to synchronize this to the humanoid mob, or not.</param>
|
|
||||||
/// <param name="humanoid">Humanoid component of the entity</param>
|
|
||||||
public void SetBaseLayerId(EntityUid uid, HumanoidVisualLayers layer, string id, bool sync = true,
|
|
||||||
HumanoidComponent? humanoid = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref humanoid)
|
|
||||||
|| !_prototypeManager.HasIndex<HumanoidSpeciesSpriteLayer>(id))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (humanoid.CustomBaseLayers.TryGetValue(layer, out var info))
|
|
||||||
{
|
|
||||||
humanoid.CustomBaseLayers[layer] = new(id, info.Color);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var layerInfo = new CustomBaseLayerInfo(id, humanoid.SkinColor);
|
|
||||||
humanoid.CustomBaseLayers.Add(layer, layerInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sync)
|
|
||||||
Synchronize(uid, humanoid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the color of this humanoid mob's base layer. See <see cref="SetBaseLayerId"/> for a
|
|
||||||
/// description of how base layers work.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uid">The humanoid mob's UID.</param>
|
|
||||||
/// <param name="layer">The layer to target on this humanoid mob.</param>
|
|
||||||
/// <param name="color">The color to set this base layer to.</param>
|
|
||||||
public void SetBaseLayerColor(EntityUid uid, HumanoidVisualLayers layer, Color color, bool sync = true, HumanoidComponent? humanoid = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref humanoid))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (humanoid.CustomBaseLayers.TryGetValue(layer, out var info))
|
|
||||||
{
|
|
||||||
humanoid.CustomBaseLayers[layer] = new(info.ID, color);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var layerInfo = new CustomBaseLayerInfo(string.Empty, color);
|
|
||||||
humanoid.CustomBaseLayers.Add(layer, layerInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sync)
|
|
||||||
Synchronize(uid, humanoid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Toggles a humanoid's sprite layer visibility.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uid">Humanoid mob's UID</param>
|
|
||||||
/// <param name="layer">Layer to toggle visibility for</param>
|
|
||||||
/// <param name="humanoid">Humanoid component of the entity</param>
|
|
||||||
public void ToggleHiddenLayer(EntityUid uid, HumanoidVisualLayers layer, HumanoidComponent? humanoid = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref humanoid, false))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (humanoid.HiddenLayers.Contains(layer))
|
|
||||||
{
|
|
||||||
humanoid.HiddenLayers.Remove(layer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
humanoid.HiddenLayers.Add(layer);
|
|
||||||
}
|
|
||||||
|
|
||||||
Synchronize(uid, humanoid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the visibility for multiple layers at once on a humanoid's sprite.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uid">Humanoid mob's UID</param>
|
|
||||||
/// <param name="layers">An enumerable of all sprite layers that are going to have their visibility set</param>
|
|
||||||
/// <param name="visible">The visibility state of the layers given</param>
|
|
||||||
/// <param name="permanent">If this is a permanent change, or temporary. Permanent layers are stored in their own hash set.</param>
|
|
||||||
/// <param name="humanoid">Humanoid component of the entity</param>
|
|
||||||
public void SetLayersVisibility(EntityUid uid, IEnumerable<HumanoidVisualLayers> layers, bool visible, bool permanent = false,
|
|
||||||
HumanoidComponent? humanoid = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref humanoid))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var layer in layers)
|
|
||||||
{
|
|
||||||
if (visible)
|
|
||||||
{
|
|
||||||
if (permanent && humanoid.PermanentlyHidden.Contains(layer))
|
|
||||||
{
|
|
||||||
humanoid.PermanentlyHidden.Remove(layer);
|
|
||||||
}
|
|
||||||
|
|
||||||
humanoid.HiddenLayers.Remove(layer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (permanent)
|
|
||||||
{
|
|
||||||
humanoid.PermanentlyHidden.Add(layer);
|
|
||||||
}
|
|
||||||
|
|
||||||
humanoid.HiddenLayers.Add(layer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Synchronize(uid, humanoid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -334,7 +143,7 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
/// <param name="sync">Whether to immediately sync this marking or not</param>
|
/// <param name="sync">Whether to immediately sync this marking or not</param>
|
||||||
/// <param name="forced">If this marking was forced (ignores marking points)</param>
|
/// <param name="forced">If this marking was forced (ignores marking points)</param>
|
||||||
/// <param name="humanoid">Humanoid component of the entity</param>
|
/// <param name="humanoid">Humanoid component of the entity</param>
|
||||||
public void AddMarking(EntityUid uid, string marking, Color? color = null, bool sync = true, bool forced = false, HumanoidComponent? humanoid = null)
|
public void AddMarking(EntityUid uid, string marking, Color? color = null, bool sync = true, bool forced = false, HumanoidAppearanceComponent? humanoid = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref humanoid)
|
if (!Resolve(uid, ref humanoid)
|
||||||
|| !_markingManager.Markings.TryGetValue(marking, out var prototype))
|
|| !_markingManager.Markings.TryGetValue(marking, out var prototype))
|
||||||
@@ -352,10 +161,10 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
humanoid.CurrentMarkings.AddBack(prototype.MarkingCategory, markingObject);
|
humanoid.MarkingSet.AddBack(prototype.MarkingCategory, markingObject);
|
||||||
|
|
||||||
if (sync)
|
if (sync)
|
||||||
Synchronize(uid, humanoid);
|
Dirty(humanoid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -367,7 +176,7 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
/// <param name="sync">Whether to immediately sync this marking or not</param>
|
/// <param name="sync">Whether to immediately sync this marking or not</param>
|
||||||
/// <param name="forced">If this marking was forced (ignores marking points)</param>
|
/// <param name="forced">If this marking was forced (ignores marking points)</param>
|
||||||
/// <param name="humanoid">Humanoid component of the entity</param>
|
/// <param name="humanoid">Humanoid component of the entity</param>
|
||||||
public void AddMarking(EntityUid uid, string marking, IReadOnlyList<Color> colors, bool sync = true, bool forced = false, HumanoidComponent? humanoid = null)
|
public void AddMarking(EntityUid uid, string marking, IReadOnlyList<Color> colors, bool sync = true, bool forced = false, HumanoidAppearanceComponent? humanoid = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref humanoid)
|
if (!Resolve(uid, ref humanoid)
|
||||||
|| !_markingManager.Markings.TryGetValue(marking, out var prototype))
|
|| !_markingManager.Markings.TryGetValue(marking, out var prototype))
|
||||||
@@ -377,10 +186,10 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
|
|
||||||
var markingObject = new Marking(marking, colors);
|
var markingObject = new Marking(marking, colors);
|
||||||
markingObject.Forced = forced;
|
markingObject.Forced = forced;
|
||||||
humanoid.CurrentMarkings.AddBack(prototype.MarkingCategory, markingObject);
|
humanoid.MarkingSet.AddBack(prototype.MarkingCategory, markingObject);
|
||||||
|
|
||||||
if (sync)
|
if (sync)
|
||||||
Synchronize(uid, humanoid);
|
Dirty(humanoid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -390,7 +199,7 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
/// <param name="marking">The marking to try and remove.</param>
|
/// <param name="marking">The marking to try and remove.</param>
|
||||||
/// <param name="sync">Whether to immediately sync this to the humanoid</param>
|
/// <param name="sync">Whether to immediately sync this to the humanoid</param>
|
||||||
/// <param name="humanoid">Humanoid component of the entity</param>
|
/// <param name="humanoid">Humanoid component of the entity</param>
|
||||||
public void RemoveMarking(EntityUid uid, string marking, bool sync = true, HumanoidComponent? humanoid = null)
|
public void RemoveMarking(EntityUid uid, string marking, bool sync = true, HumanoidAppearanceComponent? humanoid = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref humanoid)
|
if (!Resolve(uid, ref humanoid)
|
||||||
|| !_markingManager.Markings.TryGetValue(marking, out var prototype))
|
|| !_markingManager.Markings.TryGetValue(marking, out var prototype))
|
||||||
@@ -398,10 +207,10 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
humanoid.CurrentMarkings.Remove(prototype.MarkingCategory, marking);
|
humanoid.MarkingSet.Remove(prototype.MarkingCategory, marking);
|
||||||
|
|
||||||
if (sync)
|
if (sync)
|
||||||
Synchronize(uid, humanoid);
|
Dirty(humanoid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -411,19 +220,18 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
/// <param name="category">Category of the marking</param>
|
/// <param name="category">Category of the marking</param>
|
||||||
/// <param name="index">Index of the marking</param>
|
/// <param name="index">Index of the marking</param>
|
||||||
/// <param name="humanoid">Humanoid component of the entity</param>
|
/// <param name="humanoid">Humanoid component of the entity</param>
|
||||||
public void RemoveMarking(EntityUid uid, MarkingCategories category, int index, HumanoidComponent? humanoid = null)
|
public void RemoveMarking(EntityUid uid, MarkingCategories category, int index, HumanoidAppearanceComponent? humanoid = null)
|
||||||
{
|
{
|
||||||
if (index < 0
|
if (index < 0
|
||||||
|| !Resolve(uid, ref humanoid)
|
|| !Resolve(uid, ref humanoid)
|
||||||
|| !humanoid.CurrentMarkings.TryGetCategory(category, out var markings)
|
|| !humanoid.MarkingSet.TryGetCategory(category, out var markings)
|
||||||
|| index >= markings.Count)
|
|| index >= markings.Count)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
humanoid.CurrentMarkings.Remove(category, index);
|
humanoid.MarkingSet.Remove(category, index);
|
||||||
|
Dirty(humanoid);
|
||||||
Synchronize(uid, humanoid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -434,12 +242,12 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
/// <param name="index">Index of the marking</param>
|
/// <param name="index">Index of the marking</param>
|
||||||
/// <param name="markingId">The marking ID to use</param>
|
/// <param name="markingId">The marking ID to use</param>
|
||||||
/// <param name="humanoid">Humanoid component of the entity</param>
|
/// <param name="humanoid">Humanoid component of the entity</param>
|
||||||
public void SetMarkingId(EntityUid uid, MarkingCategories category, int index, string markingId, HumanoidComponent? humanoid = null)
|
public void SetMarkingId(EntityUid uid, MarkingCategories category, int index, string markingId, HumanoidAppearanceComponent? humanoid = null)
|
||||||
{
|
{
|
||||||
if (index < 0
|
if (index < 0
|
||||||
|| !_markingManager.MarkingsByCategory(category).TryGetValue(markingId, out var markingPrototype)
|
|| !_markingManager.MarkingsByCategory(category).TryGetValue(markingId, out var markingPrototype)
|
||||||
|| !Resolve(uid, ref humanoid)
|
|| !Resolve(uid, ref humanoid)
|
||||||
|| !humanoid.CurrentMarkings.TryGetCategory(category, out var markings)
|
|| !humanoid.MarkingSet.TryGetCategory(category, out var markings)
|
||||||
|| index >= markings.Count)
|
|| index >= markings.Count)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -451,9 +259,8 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
marking.SetColor(i, markings[index].MarkingColors[i]);
|
marking.SetColor(i, markings[index].MarkingColors[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
humanoid.CurrentMarkings.Replace(category, index, marking);
|
humanoid.MarkingSet.Replace(category, index, marking);
|
||||||
|
Dirty(humanoid);
|
||||||
Synchronize(uid, humanoid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -465,11 +272,11 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
/// <param name="colors">The marking colors to use</param>
|
/// <param name="colors">The marking colors to use</param>
|
||||||
/// <param name="humanoid">Humanoid component of the entity</param>
|
/// <param name="humanoid">Humanoid component of the entity</param>
|
||||||
public void SetMarkingColor(EntityUid uid, MarkingCategories category, int index, List<Color> colors,
|
public void SetMarkingColor(EntityUid uid, MarkingCategories category, int index, List<Color> colors,
|
||||||
HumanoidComponent? humanoid = null)
|
HumanoidAppearanceComponent? humanoid = null)
|
||||||
{
|
{
|
||||||
if (index < 0
|
if (index < 0
|
||||||
|| !Resolve(uid, ref humanoid)
|
|| !Resolve(uid, ref humanoid)
|
||||||
|| !humanoid.CurrentMarkings.TryGetCategory(category, out var markings)
|
|| !humanoid.MarkingSet.TryGetCategory(category, out var markings)
|
||||||
|| index >= markings.Count)
|
|| index >= markings.Count)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -480,7 +287,7 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
markings[index].SetColor(i, colors[i]);
|
markings[index].SetColor(i, colors[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Synchronize(uid, humanoid);
|
Dirty(humanoid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -521,13 +328,13 @@ public sealed partial class HumanoidSystem : SharedHumanoidSystem
|
|||||||
return Loc.GetString("identity-age-old");
|
return Loc.GetString("identity-age-old");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnsureDefaultMarkings(EntityUid uid, HumanoidComponent? humanoid)
|
private void EnsureDefaultMarkings(EntityUid uid, HumanoidAppearanceComponent? humanoid)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref humanoid))
|
if (!Resolve(uid, ref humanoid))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
humanoid.CurrentMarkings.EnsureDefault(humanoid.SkinColor, _markingManager);
|
humanoid.MarkingSet.EnsureDefault(humanoid.SkinColor, _markingManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,12 +7,12 @@ using Robust.Server.Player;
|
|||||||
|
|
||||||
namespace Content.Server.Humanoid;
|
namespace Content.Server.Humanoid;
|
||||||
|
|
||||||
public sealed partial class HumanoidSystem
|
public sealed partial class HumanoidAppearanceSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IAdminManager _adminManager = default!;
|
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||||
|
|
||||||
private void OnVerbsRequest(EntityUid uid, HumanoidComponent component, GetVerbsEvent<Verb> args)
|
private void OnVerbsRequest(EntityUid uid, HumanoidAppearanceComponent component, GetVerbsEvent<Verb> args)
|
||||||
{
|
{
|
||||||
if (!TryComp<ActorComponent>(args.User, out var actor))
|
if (!TryComp<ActorComponent>(args.User, out var actor))
|
||||||
{
|
{
|
||||||
@@ -35,12 +35,12 @@ public sealed partial class HumanoidSystem
|
|||||||
_uiSystem.TrySetUiState(
|
_uiSystem.TrySetUiState(
|
||||||
uid,
|
uid,
|
||||||
HumanoidMarkingModifierKey.Key,
|
HumanoidMarkingModifierKey.Key,
|
||||||
new HumanoidMarkingModifierState(component.CurrentMarkings, component.Species, component.SkinColor, component.CustomBaseLayers));
|
new HumanoidMarkingModifierState(component.MarkingSet, component.Species, component.SkinColor, component.CustomBaseLayers));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBaseLayersSet(EntityUid uid, HumanoidComponent component,
|
private void OnBaseLayersSet(EntityUid uid, HumanoidAppearanceComponent component,
|
||||||
HumanoidMarkingModifierBaseLayersSetMessage message)
|
HumanoidMarkingModifierBaseLayersSetMessage message)
|
||||||
{
|
{
|
||||||
if (message.Session is not IPlayerSession player
|
if (message.Session is not IPlayerSession player
|
||||||
@@ -55,24 +55,21 @@ public sealed partial class HumanoidSystem
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!component.CustomBaseLayers.TryAdd(message.Layer, message.Info))
|
component.CustomBaseLayers[message.Layer] = message.Info.Value;
|
||||||
{
|
|
||||||
component.CustomBaseLayers[message.Layer] = message.Info;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Synchronize(uid, component);
|
Dirty(component);
|
||||||
|
|
||||||
if (message.ResendState)
|
if (message.ResendState)
|
||||||
{
|
{
|
||||||
_uiSystem.TrySetUiState(
|
_uiSystem.TrySetUiState(
|
||||||
uid,
|
uid,
|
||||||
HumanoidMarkingModifierKey.Key,
|
HumanoidMarkingModifierKey.Key,
|
||||||
new HumanoidMarkingModifierState(component.CurrentMarkings, component.Species, component.SkinColor, component.CustomBaseLayers));
|
new HumanoidMarkingModifierState(component.MarkingSet, component.Species, component.SkinColor, component.CustomBaseLayers));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMarkingsSet(EntityUid uid, HumanoidComponent component,
|
private void OnMarkingsSet(EntityUid uid, HumanoidAppearanceComponent component,
|
||||||
HumanoidMarkingModifierMarkingSetMessage message)
|
HumanoidMarkingModifierMarkingSetMessage message)
|
||||||
{
|
{
|
||||||
if (message.Session is not IPlayerSession player
|
if (message.Session is not IPlayerSession player
|
||||||
@@ -81,15 +78,15 @@ public sealed partial class HumanoidSystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
component.CurrentMarkings = message.MarkingSet;
|
component.MarkingSet = message.MarkingSet;
|
||||||
Synchronize(uid, component);
|
Dirty(component);
|
||||||
|
|
||||||
if (message.ResendState)
|
if (message.ResendState)
|
||||||
{
|
{
|
||||||
_uiSystem.TrySetUiState(
|
_uiSystem.TrySetUiState(
|
||||||
uid,
|
uid,
|
||||||
HumanoidMarkingModifierKey.Key,
|
HumanoidMarkingModifierKey.Key,
|
||||||
new HumanoidMarkingModifierState(component.CurrentMarkings, component.Species, component.SkinColor, component.CustomBaseLayers));
|
new HumanoidMarkingModifierState(component.MarkingSet, component.Species, component.SkinColor, component.CustomBaseLayers));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Server.CharacterAppearance.Components;
|
using Content.Server.CharacterAppearance.Components;
|
||||||
using Content.Shared.Humanoid;
|
using Content.Shared.Humanoid;
|
||||||
using Content.Shared.Preferences;
|
using Content.Shared.Preferences;
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ namespace Content.Server.Humanoid.Systems;
|
|||||||
|
|
||||||
public sealed class RandomHumanoidAppearanceSystem : EntitySystem
|
public sealed class RandomHumanoidAppearanceSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly HumanoidSystem _humanoid = default!;
|
[Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -18,7 +18,7 @@ public sealed class RandomHumanoidAppearanceSystem : EntitySystem
|
|||||||
private void OnMapInit(EntityUid uid, RandomHumanoidAppearanceComponent component, MapInitEvent args)
|
private void OnMapInit(EntityUid uid, RandomHumanoidAppearanceComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
// If we have an initial profile/base layer set, do not randomize this humanoid.
|
// 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 HumanoidAppearanceComponent? humanoid) || !string.IsNullOrEmpty(humanoid.Initial))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public sealed class RandomHumanoidSystem : EntitySystem
|
|||||||
[Dependency] private readonly IComponentFactory _compFactory = default!;
|
[Dependency] private readonly IComponentFactory _compFactory = default!;
|
||||||
[Dependency] private readonly ISerializationManager _serialization = default!;
|
[Dependency] private readonly ISerializationManager _serialization = default!;
|
||||||
|
|
||||||
[Dependency] private readonly HumanoidSystem _humanoid = default!;
|
[Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ public class IdentitySystem : SharedIdentitySystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private IdentityRepresentation GetIdentityRepresentation(EntityUid target,
|
private IdentityRepresentation GetIdentityRepresentation(EntityUid target,
|
||||||
InventoryComponent? inventory=null,
|
InventoryComponent? inventory=null,
|
||||||
HumanoidComponent? appearance=null)
|
HumanoidAppearanceComponent? appearance=null)
|
||||||
{
|
{
|
||||||
int age = 18;
|
int age = 18;
|
||||||
Gender gender = Gender.Epicene;
|
Gender gender = Gender.Epicene;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace Content.Server.MagicMirror;
|
|||||||
public sealed class MagicMirrorSystem : EntitySystem
|
public sealed class MagicMirrorSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly MarkingManager _markings = default!;
|
[Dependency] private readonly MarkingManager _markings = default!;
|
||||||
[Dependency] private readonly HumanoidSystem _humanoid = default!;
|
[Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
|
||||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||||
|
|
||||||
|
|
||||||
@@ -30,14 +30,14 @@ public sealed class MagicMirrorSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnOpenUIAttempt(EntityUid uid, MagicMirrorComponent mirror, ActivatableUIOpenAttemptEvent args)
|
private void OnOpenUIAttempt(EntityUid uid, MagicMirrorComponent mirror, ActivatableUIOpenAttemptEvent args)
|
||||||
{
|
{
|
||||||
if (!HasComp<HumanoidComponent>(args.User))
|
if (!HasComp<HumanoidAppearanceComponent>(args.User))
|
||||||
args.Cancel();
|
args.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMagicMirrorSelect(EntityUid uid, MagicMirrorComponent component,
|
private void OnMagicMirrorSelect(EntityUid uid, MagicMirrorComponent component,
|
||||||
MagicMirrorSelectMessage message)
|
MagicMirrorSelectMessage message)
|
||||||
{
|
{
|
||||||
if (message.Session.AttachedEntity == null || !TryComp<HumanoidComponent>(message.Session.AttachedEntity.Value, out var humanoid))
|
if (message.Session.AttachedEntity == null || !TryComp<HumanoidAppearanceComponent>(message.Session.AttachedEntity.Value, out var humanoid))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,7 @@ public sealed class MagicMirrorSystem : EntitySystem
|
|||||||
private void OnMagicMirrorChangeColor(EntityUid uid, MagicMirrorComponent component,
|
private void OnMagicMirrorChangeColor(EntityUid uid, MagicMirrorComponent component,
|
||||||
MagicMirrorChangeColorMessage message)
|
MagicMirrorChangeColorMessage message)
|
||||||
{
|
{
|
||||||
if (message.Session.AttachedEntity == null || !TryComp<HumanoidComponent>(message.Session.AttachedEntity.Value, out var humanoid))
|
if (message.Session.AttachedEntity == null || !TryComp<HumanoidAppearanceComponent>(message.Session.AttachedEntity.Value, out var humanoid))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ public sealed class MagicMirrorSystem : EntitySystem
|
|||||||
private void OnMagicMirrorRemoveSlot(EntityUid uid, MagicMirrorComponent component,
|
private void OnMagicMirrorRemoveSlot(EntityUid uid, MagicMirrorComponent component,
|
||||||
MagicMirrorRemoveSlotMessage message)
|
MagicMirrorRemoveSlotMessage message)
|
||||||
{
|
{
|
||||||
if (message.Session.AttachedEntity == null || !TryComp<HumanoidComponent>(message.Session.AttachedEntity.Value, out var humanoid))
|
if (message.Session.AttachedEntity == null || !TryComp<HumanoidAppearanceComponent>(message.Session.AttachedEntity.Value, out var humanoid))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -116,7 +116,7 @@ public sealed class MagicMirrorSystem : EntitySystem
|
|||||||
private void OnMagicMirrorAddSlot(EntityUid uid, MagicMirrorComponent component,
|
private void OnMagicMirrorAddSlot(EntityUid uid, MagicMirrorComponent component,
|
||||||
MagicMirrorAddSlotMessage message)
|
MagicMirrorAddSlotMessage message)
|
||||||
{
|
{
|
||||||
if (message.Session.AttachedEntity == null || !TryComp<HumanoidComponent>(message.Session.AttachedEntity.Value, out var humanoid))
|
if (message.Session.AttachedEntity == null || !TryComp<HumanoidAppearanceComponent>(message.Session.AttachedEntity.Value, out var humanoid))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -145,34 +145,34 @@ public sealed class MagicMirrorSystem : EntitySystem
|
|||||||
UpdateInterface(uid, message.Session.AttachedEntity.Value, message.Session);
|
UpdateInterface(uid, message.Session.AttachedEntity.Value, message.Session);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateInterface(EntityUid uid, EntityUid playerUid, ICommonSession session, HumanoidComponent? humanoid = null)
|
private void UpdateInterface(EntityUid uid, EntityUid playerUid, ICommonSession session, HumanoidAppearanceComponent? humanoid = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(playerUid, ref humanoid) || session is not IPlayerSession player)
|
if (!Resolve(playerUid, ref humanoid) || session is not IPlayerSession player)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hair = humanoid.CurrentMarkings.TryGetCategory(MarkingCategories.Hair, out var hairMarkings)
|
var hair = humanoid.MarkingSet.TryGetCategory(MarkingCategories.Hair, out var hairMarkings)
|
||||||
? new List<Marking>(hairMarkings)
|
? new List<Marking>(hairMarkings)
|
||||||
: new();
|
: new();
|
||||||
|
|
||||||
var facialHair = humanoid.CurrentMarkings.TryGetCategory(MarkingCategories.FacialHair, out var facialHairMarkings)
|
var facialHair = humanoid.MarkingSet.TryGetCategory(MarkingCategories.FacialHair, out var facialHairMarkings)
|
||||||
? new List<Marking>(facialHairMarkings)
|
? new List<Marking>(facialHairMarkings)
|
||||||
: new();
|
: new();
|
||||||
|
|
||||||
var msg = new MagicMirrorUiData(
|
var msg = new MagicMirrorUiData(
|
||||||
humanoid.Species,
|
humanoid.Species,
|
||||||
hair,
|
hair,
|
||||||
humanoid.CurrentMarkings.PointsLeft(MarkingCategories.Hair) + hair.Count,
|
humanoid.MarkingSet.PointsLeft(MarkingCategories.Hair) + hair.Count,
|
||||||
facialHair,
|
facialHair,
|
||||||
humanoid.CurrentMarkings.PointsLeft(MarkingCategories.FacialHair) + facialHair.Count);
|
humanoid.MarkingSet.PointsLeft(MarkingCategories.FacialHair) + facialHair.Count);
|
||||||
|
|
||||||
_uiSystem.TrySendUiMessage(uid, MagicMirrorUiKey.Key, msg, player);
|
_uiSystem.TrySendUiMessage(uid, MagicMirrorUiKey.Key, msg, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AfterUIOpen(EntityUid uid, MagicMirrorComponent component, AfterActivatableUIOpenEvent args)
|
private void AfterUIOpen(EntityUid uid, MagicMirrorComponent component, AfterActivatableUIOpenEvent args)
|
||||||
{
|
{
|
||||||
var looks = Comp<HumanoidComponent>(args.User);
|
var looks = Comp<HumanoidAppearanceComponent>(args.User);
|
||||||
var actor = Comp<ActorComponent>(args.User);
|
var actor = Comp<ActorComponent>(args.User);
|
||||||
|
|
||||||
UpdateInterface(uid, args.User, args.Session);
|
UpdateInterface(uid, args.User, args.Session);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ using Robust.Shared.Random;
|
|||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
|
using Content.Shared.Humanoid;
|
||||||
|
|
||||||
namespace Content.Server.Medical.BiomassReclaimer
|
namespace Content.Server.Medical.BiomassReclaimer
|
||||||
{
|
{
|
||||||
@@ -256,7 +257,7 @@ namespace Content.Server.Medical.BiomassReclaimer
|
|||||||
|
|
||||||
// Reject souled bodies in easy mode.
|
// Reject souled bodies in easy mode.
|
||||||
if (_configManager.GetCVar(CCVars.BiomassEasyMode) &&
|
if (_configManager.GetCVar(CCVars.BiomassEasyMode) &&
|
||||||
HasComp<HumanoidComponent>(dragged) &&
|
HasComp<HumanoidAppearanceComponent>(dragged) &&
|
||||||
TryComp<MindComponent>(dragged, out var mindComp))
|
TryComp<MindComponent>(dragged, out var mindComp))
|
||||||
{
|
{
|
||||||
if (mindComp.Mind?.UserId != null && _playerManager.TryGetSessionById(mindComp.Mind.UserId.Value, out _))
|
if (mindComp.Mind?.UserId != null && _playerManager.TryGetSessionById(mindComp.Mind.UserId.Value, out _))
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace Content.Server.Polymorph.Systems
|
|||||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||||
[Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
|
[Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly HumanoidSystem _humanoid = default!;
|
[Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
|
||||||
[Dependency] private readonly ContainerSystem _container = default!;
|
[Dependency] private readonly ContainerSystem _container = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ namespace Content.Server.Preferences.Managers
|
|||||||
case HumanoidCharacterProfile hp:
|
case HumanoidCharacterProfile hp:
|
||||||
{
|
{
|
||||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
var selectedSpecies = HumanoidSystem.DefaultSpecies;
|
var selectedSpecies = HumanoidAppearanceSystem.DefaultSpecies;
|
||||||
|
|
||||||
if (prototypeManager.TryIndex<SpeciesPrototype>(hp.Species, out var species) && species.RoundStart)
|
if (prototypeManager.TryIndex<SpeciesPrototype>(hp.Species, out var species) && species.RoundStart)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ using Content.Shared.Mobs.Systems;
|
|||||||
using Content.Shared.Revenant.Components;
|
using Content.Shared.Revenant.Components;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
using Content.Shared.Humanoid;
|
||||||
|
|
||||||
namespace Content.Server.Revenant.EntitySystems;
|
namespace Content.Server.Revenant.EntitySystems;
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ public sealed partial class RevenantSystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!HasComp<MobStateComponent>(target) || !HasComp<HumanoidComponent>(target) || HasComp<RevenantComponent>(target))
|
if (!HasComp<MobStateComponent>(target) || !HasComp<HumanoidAppearanceComponent>(target) || HasComp<RevenantComponent>(target))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public sealed class VocalSystem : EntitySystem
|
|||||||
if (!_blocker.CanSpeak(uid))
|
if (!_blocker.CanSpeak(uid))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var sex = CompOrNull<HumanoidComponent>(uid)?.Sex ?? Sex.Unsexed;
|
var sex = CompOrNull<HumanoidAppearanceComponent>(uid)?.Sex ?? Sex.Unsexed;
|
||||||
|
|
||||||
if (_random.Prob(component.WilhelmProbability))
|
if (_random.Prob(component.WilhelmProbability))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public sealed class StationSpawningSystem : EntitySystem
|
|||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||||
[Dependency] private readonly HandsSystem _handsSystem = default!;
|
[Dependency] private readonly HandsSystem _handsSystem = default!;
|
||||||
[Dependency] private readonly HumanoidSystem _humanoidSystem = default!;
|
[Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!;
|
||||||
[Dependency] private readonly IdCardSystem _cardSystem = default!;
|
[Dependency] private readonly IdCardSystem _cardSystem = default!;
|
||||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||||
[Dependency] private readonly PDASystem _pdaSystem = default!;
|
[Dependency] private readonly PDASystem _pdaSystem = default!;
|
||||||
@@ -106,7 +106,7 @@ public sealed class StationSpawningSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
var entity = EntityManager.SpawnEntity(
|
var entity = EntityManager.SpawnEntity(
|
||||||
_prototypeManager.Index<SpeciesPrototype>(profile?.Species ?? HumanoidSystem.DefaultSpecies).Prototype,
|
_prototypeManager.Index<SpeciesPrototype>(profile?.Species ?? HumanoidAppearanceSystem.DefaultSpecies).Prototype,
|
||||||
coordinates);
|
coordinates);
|
||||||
|
|
||||||
if (job?.StartingGear != null)
|
if (job?.StartingGear != null)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Content.Shared.Humanoid;
|
|||||||
using Content.Shared.Store;
|
using Content.Shared.Store;
|
||||||
using Content.Shared.Humanoid.Prototypes;
|
using Content.Shared.Humanoid.Prototypes;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
|
||||||
|
using Content.Shared.Humanoid;
|
||||||
|
|
||||||
namespace Content.Server.Store.Conditions;
|
namespace Content.Server.Store.Conditions;
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@ public sealed class BuyerSpeciesCondition : ListingCondition
|
|||||||
{
|
{
|
||||||
var ent = args.EntityManager;
|
var ent = args.EntityManager;
|
||||||
|
|
||||||
if (!ent.TryGetComponent<HumanoidComponent>(args.Buyer, out var appearance))
|
if (!ent.TryGetComponent<HumanoidAppearanceComponent>(args.Buyer, out var appearance))
|
||||||
return true; // inanimate or non-humanoid entities should be handled elsewhere, main example being surplus crates
|
return true; // inanimate or non-humanoid entities should be handled elsewhere, main example being surplus crates
|
||||||
|
|
||||||
if (Blacklist != null)
|
if (Blacklist != null)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace Content.Server.Zombies
|
|||||||
[Dependency] private readonly ChatSystem _chat = default!;
|
[Dependency] private readonly ChatSystem _chat = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||||
[Dependency] private readonly HumanoidSystem _humanoidSystem = default!;
|
[Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace Content.Server.Zombies
|
|||||||
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
|
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
|
||||||
[Dependency] private readonly ServerInventorySystem _serverInventory = default!;
|
[Dependency] private readonly ServerInventorySystem _serverInventory = default!;
|
||||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||||
[Dependency] private readonly HumanoidSystem _sharedHuApp = default!;
|
[Dependency] private readonly HumanoidAppearanceSystem _sharedHuApp = default!;
|
||||||
[Dependency] private readonly IdentitySystem _identity = default!;
|
[Dependency] private readonly IdentitySystem _identity = default!;
|
||||||
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
|
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
|
||||||
[Dependency] private readonly IChatManager _chatMan = default!;
|
[Dependency] private readonly IChatManager _chatMan = default!;
|
||||||
@@ -125,7 +125,7 @@ namespace Content.Server.Zombies
|
|||||||
Dirty(melee);
|
Dirty(melee);
|
||||||
|
|
||||||
//We have specific stuff for humanoid zombies because they matter more
|
//We have specific stuff for humanoid zombies because they matter more
|
||||||
if (TryComp<HumanoidComponent>(target, out var huApComp)) //huapcomp
|
if (TryComp<HumanoidAppearanceComponent>(target, out var huApComp)) //huapcomp
|
||||||
{
|
{
|
||||||
//store some values before changing them in case the humanoid get cloned later
|
//store some values before changing them in case the humanoid get cloned later
|
||||||
zombiecomp.BeforeZombifiedSkinColor = huApComp.SkinColor;
|
zombiecomp.BeforeZombifiedSkinColor = huApComp.SkinColor;
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using Content.Shared.Clothing.Components;
|
using Content.Shared.Clothing.Components;
|
||||||
|
using Content.Shared.Humanoid;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.Inventory.Events;
|
using Content.Shared.Inventory.Events;
|
||||||
using Content.Shared.Item;
|
using Content.Shared.Item;
|
||||||
|
using Content.Shared.Tag;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
namespace Content.Shared.Clothing.EntitySystems;
|
namespace Content.Shared.Clothing.EntitySystems;
|
||||||
@@ -9,6 +11,8 @@ namespace Content.Shared.Clothing.EntitySystems;
|
|||||||
public abstract class ClothingSystem : EntitySystem
|
public abstract class ClothingSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly SharedItemSystem _itemSys = default!;
|
[Dependency] private readonly SharedItemSystem _itemSys = default!;
|
||||||
|
[Dependency] private readonly SharedHumanoidAppearanceSystem _humanoidSystem = default!;
|
||||||
|
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -23,11 +27,15 @@ public abstract class ClothingSystem : EntitySystem
|
|||||||
protected virtual void OnGotEquipped(EntityUid uid, ClothingComponent component, GotEquippedEvent args)
|
protected virtual void OnGotEquipped(EntityUid uid, ClothingComponent component, GotEquippedEvent args)
|
||||||
{
|
{
|
||||||
component.InSlot = args.Slot;
|
component.InSlot = args.Slot;
|
||||||
|
if (args.Slot == "head" && _tagSystem.HasTag(args.Equipment, "HidesHair"))
|
||||||
|
_humanoidSystem.SetLayerVisibility(args.Equipee, HumanoidVisualLayers.Hair, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnGotUnequipped(EntityUid uid, ClothingComponent component, GotUnequippedEvent args)
|
protected virtual void OnGotUnequipped(EntityUid uid, ClothingComponent component, GotUnequippedEvent args)
|
||||||
{
|
{
|
||||||
component.InSlot = null;
|
component.InSlot = null;
|
||||||
|
if (args.Slot == "head" && _tagSystem.HasTag(args.Equipment, "HidesHair"))
|
||||||
|
_humanoidSystem.SetLayerVisibility(args.Equipee, HumanoidVisualLayers.Hair, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGetState(EntityUid uid, ClothingComponent component, ref ComponentGetState args)
|
private void OnGetState(EntityUid uid, ClothingComponent component, ref ComponentGetState args)
|
||||||
|
|||||||
136
Content.Shared/Humanoid/HumanoidAppearanceComponent.cs
Normal file
136
Content.Shared/Humanoid/HumanoidAppearanceComponent.cs
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
using Content.Shared.Humanoid.Markings;
|
||||||
|
using Content.Shared.Humanoid.Prototypes;
|
||||||
|
using Robust.Shared.Enums;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
using static Content.Shared.Humanoid.HumanoidAppearanceState;
|
||||||
|
|
||||||
|
namespace Content.Shared.Humanoid;
|
||||||
|
|
||||||
|
[NetworkedComponent, RegisterComponent]
|
||||||
|
public sealed class HumanoidAppearanceComponent : Component
|
||||||
|
{
|
||||||
|
[DataField("markingSet")]
|
||||||
|
public MarkingSet MarkingSet = new();
|
||||||
|
|
||||||
|
[DataField("baseLayers")]
|
||||||
|
public Dictionary<HumanoidVisualLayers, HumanoidSpeciesSpriteLayer> BaseLayers = new();
|
||||||
|
|
||||||
|
[DataField("permanentlyHidden")]
|
||||||
|
public HashSet<HumanoidVisualLayers> PermanentlyHidden = new();
|
||||||
|
|
||||||
|
// Couldn't these be somewhere else?
|
||||||
|
|
||||||
|
[DataField("gender")]
|
||||||
|
[ViewVariables] public Gender Gender = default!;
|
||||||
|
|
||||||
|
[DataField("age")]
|
||||||
|
[ViewVariables] public int Age = 18;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Any custom base layers this humanoid might have. See:
|
||||||
|
/// limb transplants (potentially), robotic arms, etc.
|
||||||
|
/// Stored on the server, this is merged in the client into
|
||||||
|
/// all layer settings.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("customBaseLayers")]
|
||||||
|
public Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> CustomBaseLayers = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current species. Dictates things like base body sprites,
|
||||||
|
/// base humanoid to spawn, etc.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("species", customTypeSerializer: typeof(PrototypeIdSerializer<SpeciesPrototype>))]
|
||||||
|
public string Species { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The initial profile and base layers to apply to this humanoid.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("initial", customTypeSerializer: typeof(PrototypeIdSerializer<HumanoidProfilePrototype>))]
|
||||||
|
public string? Initial { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Skin color of this humanoid.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("skinColor")]
|
||||||
|
public Color SkinColor { get; set; } = Color.FromHex("#C0967F");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Visual layers currently hidden. This will affect the base sprite
|
||||||
|
/// on this humanoid layer, and any markings that sit above it.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("hiddenLayers")]
|
||||||
|
public HashSet<HumanoidVisualLayers> HiddenLayers = new();
|
||||||
|
|
||||||
|
[DataField("sex")]
|
||||||
|
public Sex Sex = Sex.Male;
|
||||||
|
|
||||||
|
[DataField("eyeColor")]
|
||||||
|
public Color EyeColor = Color.Brown;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class HumanoidAppearanceState : ComponentState
|
||||||
|
{
|
||||||
|
public readonly MarkingSet Markings;
|
||||||
|
public readonly HashSet<HumanoidVisualLayers> PermanentlyHidden;
|
||||||
|
public readonly HashSet<HumanoidVisualLayers> HiddenLayers;
|
||||||
|
public readonly Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> CustomBaseLayers;
|
||||||
|
public readonly Sex Sex;
|
||||||
|
public readonly Gender Gender;
|
||||||
|
public readonly int Age = 18;
|
||||||
|
public readonly string Species;
|
||||||
|
public readonly Color SkinColor;
|
||||||
|
public readonly Color EyeColor;
|
||||||
|
|
||||||
|
public HumanoidAppearanceState(
|
||||||
|
MarkingSet currentMarkings,
|
||||||
|
HashSet<HumanoidVisualLayers> permanentlyHidden,
|
||||||
|
HashSet<HumanoidVisualLayers> hiddenLayers,
|
||||||
|
Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> customBaseLayers,
|
||||||
|
Sex sex,
|
||||||
|
Gender gender,
|
||||||
|
int age,
|
||||||
|
string species,
|
||||||
|
Color skinColor,
|
||||||
|
Color eyeColor)
|
||||||
|
{
|
||||||
|
Markings = currentMarkings;
|
||||||
|
PermanentlyHidden = permanentlyHidden;
|
||||||
|
HiddenLayers = hiddenLayers;
|
||||||
|
CustomBaseLayers = customBaseLayers;
|
||||||
|
Sex = sex;
|
||||||
|
Gender = gender;
|
||||||
|
Age = age;
|
||||||
|
Species = species;
|
||||||
|
SkinColor = skinColor;
|
||||||
|
EyeColor = eyeColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataDefinition]
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public readonly struct CustomBaseLayerInfo
|
||||||
|
{
|
||||||
|
public CustomBaseLayerInfo(string id, Color? color = null)
|
||||||
|
{
|
||||||
|
DebugTools.Assert(IoCManager.Resolve<IPrototypeManager>().HasIndex<HumanoidSpeciesSpriteLayer>(id));
|
||||||
|
ID = id;
|
||||||
|
Color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ID of this custom base layer. Must be a <see cref="HumanoidSpeciesSpriteLayer"/>.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("id", customTypeSerializer: typeof(PrototypeIdSerializer<HumanoidSpeciesSpriteLayer>), required: true)]
|
||||||
|
public string ID { init; get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Color of this custom base layer. Null implies skin colour.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("color")]
|
||||||
|
public Color? Color { init; get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
using Content.Shared.Humanoid.Markings;
|
|
||||||
using Content.Shared.Humanoid.Prototypes;
|
|
||||||
using Robust.Shared.Enums;
|
|
||||||
using Robust.Shared.GameStates;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
|
||||||
|
|
||||||
namespace Content.Shared.Humanoid;
|
|
||||||
|
|
||||||
[RegisterComponent, NetworkedComponent]
|
|
||||||
public sealed class HumanoidComponent : Component
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Current species. Dictates things like base body sprites,
|
|
||||||
/// base humanoid to spawn, etc.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("species", customTypeSerializer: typeof(PrototypeIdSerializer<SpeciesPrototype>))]
|
|
||||||
public string Species { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The initial profile and base layers to apply to this humanoid.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("initial", customTypeSerializer: typeof(PrototypeIdSerializer<HumanoidProfilePrototype>))]
|
|
||||||
public string? Initial { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Skin color of this humanoid.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("skinColor")]
|
|
||||||
public Color SkinColor { get; set; } = Color.FromHex("#C0967F");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Visual layers currently hidden. This will affect the base sprite
|
|
||||||
/// on this humanoid layer, and any markings that sit above it.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables] public readonly HashSet<HumanoidVisualLayers> HiddenLayers = new();
|
|
||||||
|
|
||||||
[DataField("sex")] public Sex Sex = Sex.Male;
|
|
||||||
|
|
||||||
public MarkingSet CurrentMarkings = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Any custom base layers this humanoid might have. See:
|
|
||||||
/// limb transplants (potentially), robotic arms, etc.
|
|
||||||
/// Stored on the server, this is merged in the client into
|
|
||||||
/// all layer settings.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadOnly)]
|
|
||||||
public Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> CustomBaseLayers = new();
|
|
||||||
|
|
||||||
public HashSet<HumanoidVisualLayers> PermanentlyHidden = new();
|
|
||||||
|
|
||||||
public HashSet<HumanoidVisualLayers> AllHiddenLayers
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var result = new HashSet<HumanoidVisualLayers>(HiddenLayers);
|
|
||||||
result.UnionWith(PermanentlyHidden);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Couldn't these be somewhere else?
|
|
||||||
[ViewVariables] public Gender Gender = default!;
|
|
||||||
[ViewVariables] public int Age = 18;
|
|
||||||
|
|
||||||
[ViewVariables] public List<Marking> CurrentClientMarkings = new();
|
|
||||||
|
|
||||||
public Dictionary<HumanoidVisualLayers, HumanoidSpeciesSpriteLayer> BaseLayers = new();
|
|
||||||
|
|
||||||
public string LastSpecies = default!;
|
|
||||||
}
|
|
||||||
|
|
||||||
[DataDefinition]
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class CustomBaseLayerInfo
|
|
||||||
{
|
|
||||||
public CustomBaseLayerInfo(string id, Color color)
|
|
||||||
{
|
|
||||||
ID = id;
|
|
||||||
Color = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID of this custom base layer. Must be a <see cref="HumanoidSpeciesSpriteLayer"/>.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("id")]
|
|
||||||
public string ID { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Color of this custom base layer.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("color")]
|
|
||||||
public Color Color { get; }
|
|
||||||
}
|
|
||||||
@@ -15,6 +15,14 @@ namespace Content.Shared.Humanoid
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetSexMorph(HumanoidVisualLayers layer, Sex sex, string id)
|
||||||
|
{
|
||||||
|
if (!HasSexMorph(layer) || sex == Sex.Unsexed)
|
||||||
|
return id;
|
||||||
|
|
||||||
|
return $"{id}{sex}";
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sublayers. Any other layers that may visually depend on this layer existing.
|
/// Sublayers. Any other layers that may visually depend on this layer existing.
|
||||||
/// For example, the head has layers such as eyes, hair, etc. depending on it.
|
/// For example, the head has layers such as eyes, hair, etc. depending on it.
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
using Content.Shared.Humanoid.Markings;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Shared.Humanoid;
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public enum HumanoidVisualizerKey
|
|
||||||
{
|
|
||||||
Key
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class HumanoidVisualizerData : ICloneable
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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, Sex, new(LayerVisibility), new(Markings));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@ using Robust.Shared.Serialization;
|
|||||||
|
|
||||||
namespace Content.Shared.Humanoid.Markings
|
namespace Content.Shared.Humanoid.Markings
|
||||||
{
|
{
|
||||||
|
[DataDefinition]
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class Marking : IEquatable<Marking>, IComparable<Marking>, IComparable<string>
|
public sealed class Marking : IEquatable<Marking>, IComparable<Marking>, IComparable<string>
|
||||||
{
|
{
|
||||||
@@ -54,7 +55,7 @@ namespace Content.Shared.Humanoid.Markings
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// If this marking is currently visible.
|
/// If this marking is currently visible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables]
|
[DataField("visible")]
|
||||||
public bool Visible = true;
|
public bool Visible = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace Content.Shared.Humanoid.Markings;
|
|||||||
/// This is serializable for the admin panel that sets markings on demand for a player.
|
/// This is serializable for the admin panel that sets markings on demand for a player.
|
||||||
/// Most APIs that accept a set of markings usually use a List of type Marking instead.
|
/// Most APIs that accept a set of markings usually use a List of type Marking instead.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
[DataDefinition]
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class MarkingSet
|
public sealed class MarkingSet
|
||||||
{
|
{
|
||||||
@@ -40,16 +41,14 @@ public sealed class MarkingSet
|
|||||||
/// feature of markings, which is the limit of markings you can put on a
|
/// feature of markings, which is the limit of markings you can put on a
|
||||||
/// humanoid.
|
/// humanoid.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private Dictionary<MarkingCategories, List<Marking>> _markings = new();
|
[DataField("markings")]
|
||||||
|
public Dictionary<MarkingCategories, List<Marking>> Markings = new();
|
||||||
// why i didn't encapsulate this in the first place, i won't know
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Marking points for each category.
|
/// Marking points for each category.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Dictionary<MarkingCategories, MarkingPoints> _points = new();
|
[DataField("points")]
|
||||||
|
public Dictionary<MarkingCategories, MarkingPoints> Points = new();
|
||||||
public IReadOnlyList<Marking> this[MarkingCategories category] => _markings[category];
|
|
||||||
|
|
||||||
public MarkingSet()
|
public MarkingSet()
|
||||||
{}
|
{}
|
||||||
@@ -71,7 +70,7 @@ public sealed class MarkingSet
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_points = MarkingPoints.CloneMarkingPointDictionary(points.Points);
|
Points = MarkingPoints.CloneMarkingPointDictionary(points.Points);
|
||||||
|
|
||||||
foreach (var marking in markings)
|
foreach (var marking in markings)
|
||||||
{
|
{
|
||||||
@@ -111,7 +110,7 @@ public sealed class MarkingSet
|
|||||||
/// <param name="other">The other marking set.</param>
|
/// <param name="other">The other marking set.</param>
|
||||||
public MarkingSet(MarkingSet other)
|
public MarkingSet(MarkingSet other)
|
||||||
{
|
{
|
||||||
foreach (var (key, list) in other._markings)
|
foreach (var (key, list) in other.Markings)
|
||||||
{
|
{
|
||||||
foreach (var marking in list)
|
foreach (var marking in list)
|
||||||
{
|
{
|
||||||
@@ -119,7 +118,7 @@ public sealed class MarkingSet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_points = MarkingPoints.CloneMarkingPointDictionary(other._points);
|
Points = MarkingPoints.CloneMarkingPointDictionary(other.Points);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -137,7 +136,7 @@ public sealed class MarkingSet
|
|||||||
var speciesProto = prototypeManager.Index<SpeciesPrototype>(species);
|
var speciesProto = prototypeManager.Index<SpeciesPrototype>(species);
|
||||||
var onlyWhitelisted = prototypeManager.Index<MarkingPointsPrototype>(speciesProto.MarkingPoints).OnlyWhitelisted;
|
var onlyWhitelisted = prototypeManager.Index<MarkingPointsPrototype>(speciesProto.MarkingPoints).OnlyWhitelisted;
|
||||||
|
|
||||||
foreach (var (category, list) in _markings)
|
foreach (var (category, list) in Markings)
|
||||||
{
|
{
|
||||||
foreach (var marking in list)
|
foreach (var marking in list)
|
||||||
{
|
{
|
||||||
@@ -175,7 +174,7 @@ public sealed class MarkingSet
|
|||||||
IoCManager.Resolve(ref markingManager);
|
IoCManager.Resolve(ref markingManager);
|
||||||
|
|
||||||
var toRemove = new List<int>();
|
var toRemove = new List<int>();
|
||||||
foreach (var (category, list) in _markings)
|
foreach (var (category, list) in Markings)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < list.Count; i++)
|
for (var i = 0; i < list.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -207,7 +206,7 @@ public sealed class MarkingSet
|
|||||||
{
|
{
|
||||||
IoCManager.Resolve(ref markingManager);
|
IoCManager.Resolve(ref markingManager);
|
||||||
|
|
||||||
foreach (var (category, points) in _points)
|
foreach (var (category, points) in Points)
|
||||||
{
|
{
|
||||||
if (points.Points <= 0 || points.DefaultMarkings.Count <= 0)
|
if (points.Points <= 0 || points.DefaultMarkings.Count <= 0)
|
||||||
{
|
{
|
||||||
@@ -251,7 +250,7 @@ public sealed class MarkingSet
|
|||||||
/// <returns>A number equal or greater than zero if the category exists, -1 otherwise.</returns>
|
/// <returns>A number equal or greater than zero if the category exists, -1 otherwise.</returns>
|
||||||
public int PointsLeft(MarkingCategories category)
|
public int PointsLeft(MarkingCategories category)
|
||||||
{
|
{
|
||||||
if (!_points.TryGetValue(category, out var points))
|
if (!Points.TryGetValue(category, out var points))
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -266,7 +265,7 @@ public sealed class MarkingSet
|
|||||||
/// <param name="marking">The marking instance in question.</param>
|
/// <param name="marking">The marking instance in question.</param>
|
||||||
public void AddFront(MarkingCategories category, Marking marking)
|
public void AddFront(MarkingCategories category, Marking marking)
|
||||||
{
|
{
|
||||||
if (!marking.Forced && _points.TryGetValue(category, out var points))
|
if (!marking.Forced && Points.TryGetValue(category, out var points))
|
||||||
{
|
{
|
||||||
if (points.Points <= 0)
|
if (points.Points <= 0)
|
||||||
{
|
{
|
||||||
@@ -276,10 +275,10 @@ public sealed class MarkingSet
|
|||||||
points.Points--;
|
points.Points--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_markings.TryGetValue(category, out var markings))
|
if (!Markings.TryGetValue(category, out var markings))
|
||||||
{
|
{
|
||||||
markings = new();
|
markings = new();
|
||||||
_markings[category] = markings;
|
Markings[category] = markings;
|
||||||
}
|
}
|
||||||
|
|
||||||
markings.Insert(0, marking);
|
markings.Insert(0, marking);
|
||||||
@@ -292,7 +291,7 @@ public sealed class MarkingSet
|
|||||||
/// <param name="marking"></param>
|
/// <param name="marking"></param>
|
||||||
public void AddBack(MarkingCategories category, Marking marking)
|
public void AddBack(MarkingCategories category, Marking marking)
|
||||||
{
|
{
|
||||||
if (!marking.Forced && _points.TryGetValue(category, out var points))
|
if (!marking.Forced && Points.TryGetValue(category, out var points))
|
||||||
{
|
{
|
||||||
if (points.Points <= 0)
|
if (points.Points <= 0)
|
||||||
{
|
{
|
||||||
@@ -302,10 +301,10 @@ public sealed class MarkingSet
|
|||||||
points.Points--;
|
points.Points--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_markings.TryGetValue(category, out var markings))
|
if (!Markings.TryGetValue(category, out var markings))
|
||||||
{
|
{
|
||||||
markings = new();
|
markings = new();
|
||||||
_markings[category] = markings;
|
Markings[category] = markings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -320,7 +319,7 @@ public sealed class MarkingSet
|
|||||||
public List<Marking> AddCategory(MarkingCategories category)
|
public List<Marking> AddCategory(MarkingCategories category)
|
||||||
{
|
{
|
||||||
var markings = new List<Marking>();
|
var markings = new List<Marking>();
|
||||||
_markings.Add(category, markings);
|
Markings.Add(category, markings);
|
||||||
return markings;
|
return markings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,7 +331,7 @@ public sealed class MarkingSet
|
|||||||
/// <param name="marking">The marking to insert.</param>
|
/// <param name="marking">The marking to insert.</param>
|
||||||
public void Replace(MarkingCategories category, int index, Marking marking)
|
public void Replace(MarkingCategories category, int index, Marking marking)
|
||||||
{
|
{
|
||||||
if (index < 0 || !_markings.TryGetValue(category, out var markings)
|
if (index < 0 || !Markings.TryGetValue(category, out var markings)
|
||||||
|| index >= markings.Count)
|
|| index >= markings.Count)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -349,7 +348,7 @@ public sealed class MarkingSet
|
|||||||
/// <returns>True if removed, false otherwise.</returns>
|
/// <returns>True if removed, false otherwise.</returns>
|
||||||
public bool Remove(MarkingCategories category, string id)
|
public bool Remove(MarkingCategories category, string id)
|
||||||
{
|
{
|
||||||
if (!_markings.TryGetValue(category, out var markings))
|
if (!Markings.TryGetValue(category, out var markings))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -361,7 +360,7 @@ public sealed class MarkingSet
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!markings[i].Forced && _points.TryGetValue(category, out var points))
|
if (!markings[i].Forced && Points.TryGetValue(category, out var points))
|
||||||
{
|
{
|
||||||
points.Points++;
|
points.Points++;
|
||||||
}
|
}
|
||||||
@@ -381,7 +380,7 @@ public sealed class MarkingSet
|
|||||||
/// <returns>True if removed, false otherwise.</returns>
|
/// <returns>True if removed, false otherwise.</returns>
|
||||||
public void Remove(MarkingCategories category, int idx)
|
public void Remove(MarkingCategories category, int idx)
|
||||||
{
|
{
|
||||||
if (!_markings.TryGetValue(category, out var markings))
|
if (!Markings.TryGetValue(category, out var markings))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -391,7 +390,7 @@ public sealed class MarkingSet
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!markings[idx].Forced && _points.TryGetValue(category, out var points))
|
if (!markings[idx].Forced && Points.TryGetValue(category, out var points))
|
||||||
{
|
{
|
||||||
points.Points++;
|
points.Points++;
|
||||||
}
|
}
|
||||||
@@ -406,12 +405,12 @@ public sealed class MarkingSet
|
|||||||
/// <returns>True if removed, false otherwise.</returns>
|
/// <returns>True if removed, false otherwise.</returns>
|
||||||
public bool RemoveCategory(MarkingCategories category)
|
public bool RemoveCategory(MarkingCategories category)
|
||||||
{
|
{
|
||||||
if (!_markings.TryGetValue(category, out var markings))
|
if (!Markings.TryGetValue(category, out var markings))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_points.TryGetValue(category, out var points))
|
if (Points.TryGetValue(category, out var points))
|
||||||
{
|
{
|
||||||
foreach (var marking in markings)
|
foreach (var marking in markings)
|
||||||
{
|
{
|
||||||
@@ -424,7 +423,7 @@ public sealed class MarkingSet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_markings.Remove(category);
|
Markings.Remove(category);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,7 +446,7 @@ public sealed class MarkingSet
|
|||||||
/// <returns>The index of the marking, otherwise a negative number.</returns>
|
/// <returns>The index of the marking, otherwise a negative number.</returns>
|
||||||
public int FindIndexOf(MarkingCategories category, string id)
|
public int FindIndexOf(MarkingCategories category, string id)
|
||||||
{
|
{
|
||||||
if (!_markings.TryGetValue(category, out var markings))
|
if (!Markings.TryGetValue(category, out var markings))
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -465,7 +464,7 @@ public sealed class MarkingSet
|
|||||||
{
|
{
|
||||||
markings = null;
|
markings = null;
|
||||||
|
|
||||||
if (_markings.TryGetValue(category, out var list))
|
if (Markings.TryGetValue(category, out var list))
|
||||||
{
|
{
|
||||||
markings = list;
|
markings = list;
|
||||||
return true;
|
return true;
|
||||||
@@ -485,7 +484,7 @@ public sealed class MarkingSet
|
|||||||
{
|
{
|
||||||
marking = null;
|
marking = null;
|
||||||
|
|
||||||
if (!_markings.TryGetValue(category, out var markings))
|
if (!Markings.TryGetValue(category, out var markings))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -509,7 +508,7 @@ public sealed class MarkingSet
|
|||||||
/// <param name="idx">Index of the marking.</param>
|
/// <param name="idx">Index of the marking.</param>
|
||||||
public void ShiftRankUp(MarkingCategories category, int idx)
|
public void ShiftRankUp(MarkingCategories category, int idx)
|
||||||
{
|
{
|
||||||
if (!_markings.TryGetValue(category, out var markings))
|
if (!Markings.TryGetValue(category, out var markings))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -529,7 +528,7 @@ public sealed class MarkingSet
|
|||||||
/// <param name="idx">Index of the marking from the end</param>
|
/// <param name="idx">Index of the marking from the end</param>
|
||||||
public void ShiftRankUpFromEnd(MarkingCategories category, int idx)
|
public void ShiftRankUpFromEnd(MarkingCategories category, int idx)
|
||||||
{
|
{
|
||||||
if (!_markings.TryGetValue(category, out var markings))
|
if (!Markings.TryGetValue(category, out var markings))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -544,7 +543,7 @@ public sealed class MarkingSet
|
|||||||
/// <param name="idx">Index of the marking.</param>
|
/// <param name="idx">Index of the marking.</param>
|
||||||
public void ShiftRankDown(MarkingCategories category, int idx)
|
public void ShiftRankDown(MarkingCategories category, int idx)
|
||||||
{
|
{
|
||||||
if (!_markings.TryGetValue(category, out var markings))
|
if (!Markings.TryGetValue(category, out var markings))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -564,7 +563,7 @@ public sealed class MarkingSet
|
|||||||
/// <param name="idx">Index of the marking from the end</param>
|
/// <param name="idx">Index of the marking from the end</param>
|
||||||
public void ShiftRankDownFromEnd(MarkingCategories category, int idx)
|
public void ShiftRankDownFromEnd(MarkingCategories category, int idx)
|
||||||
{
|
{
|
||||||
if (!_markings.TryGetValue(category, out var markings))
|
if (!Markings.TryGetValue(category, out var markings))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -579,7 +578,7 @@ public sealed class MarkingSet
|
|||||||
public ForwardMarkingEnumerator GetForwardEnumerator()
|
public ForwardMarkingEnumerator GetForwardEnumerator()
|
||||||
{
|
{
|
||||||
var markings = new List<Marking>();
|
var markings = new List<Marking>();
|
||||||
foreach (var (_, list) in _markings)
|
foreach (var (_, list) in Markings)
|
||||||
{
|
{
|
||||||
markings.AddRange(list);
|
markings.AddRange(list);
|
||||||
}
|
}
|
||||||
@@ -595,7 +594,7 @@ public sealed class MarkingSet
|
|||||||
public ForwardMarkingEnumerator GetForwardEnumerator(MarkingCategories category)
|
public ForwardMarkingEnumerator GetForwardEnumerator(MarkingCategories category)
|
||||||
{
|
{
|
||||||
var markings = new List<Marking>();
|
var markings = new List<Marking>();
|
||||||
if (_markings.TryGetValue(category, out var listing))
|
if (Markings.TryGetValue(category, out var listing))
|
||||||
{
|
{
|
||||||
markings = new(listing);
|
markings = new(listing);
|
||||||
}
|
}
|
||||||
@@ -610,7 +609,7 @@ public sealed class MarkingSet
|
|||||||
public ReverseMarkingEnumerator GetReverseEnumerator()
|
public ReverseMarkingEnumerator GetReverseEnumerator()
|
||||||
{
|
{
|
||||||
var markings = new List<Marking>();
|
var markings = new List<Marking>();
|
||||||
foreach (var (_, list) in _markings)
|
foreach (var (_, list) in Markings)
|
||||||
{
|
{
|
||||||
markings.AddRange(list);
|
markings.AddRange(list);
|
||||||
}
|
}
|
||||||
@@ -626,7 +625,7 @@ public sealed class MarkingSet
|
|||||||
public ReverseMarkingEnumerator GetReverseEnumerator(MarkingCategories category)
|
public ReverseMarkingEnumerator GetReverseEnumerator(MarkingCategories category)
|
||||||
{
|
{
|
||||||
var markings = new List<Marking>();
|
var markings = new List<Marking>();
|
||||||
if (_markings.TryGetValue(category, out var listing))
|
if (Markings.TryGetValue(category, out var listing))
|
||||||
{
|
{
|
||||||
markings = new(listing);
|
markings = new(listing);
|
||||||
}
|
}
|
||||||
@@ -636,8 +635,8 @@ public sealed class MarkingSet
|
|||||||
|
|
||||||
public bool CategoryEquals(MarkingCategories category, MarkingSet other)
|
public bool CategoryEquals(MarkingCategories category, MarkingSet other)
|
||||||
{
|
{
|
||||||
if (!_markings.TryGetValue(category, out var markings)
|
if (!Markings.TryGetValue(category, out var markings)
|
||||||
|| !other._markings.TryGetValue(category, out var markingsOther))
|
|| !other.Markings.TryGetValue(category, out var markingsOther))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -647,7 +646,7 @@ public sealed class MarkingSet
|
|||||||
|
|
||||||
public bool Equals(MarkingSet other)
|
public bool Equals(MarkingSet other)
|
||||||
{
|
{
|
||||||
foreach (var (category, _) in _markings)
|
foreach (var (category, _) in Markings)
|
||||||
{
|
{
|
||||||
if (!CategoryEquals(category, other))
|
if (!CategoryEquals(category, other))
|
||||||
{
|
{
|
||||||
@@ -665,7 +664,7 @@ public sealed class MarkingSet
|
|||||||
/// <returns>Enumerator of marking categories that were different between the two.</returns>
|
/// <returns>Enumerator of marking categories that were different between the two.</returns>
|
||||||
public IEnumerable<MarkingCategories> CategoryDifference(MarkingSet other)
|
public IEnumerable<MarkingCategories> CategoryDifference(MarkingSet other)
|
||||||
{
|
{
|
||||||
foreach (var (category, _) in _markings)
|
foreach (var (category, _) in Markings)
|
||||||
{
|
{
|
||||||
if (!CategoryEquals(category, other))
|
if (!CategoryEquals(category, other))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Content.Shared.Preferences;
|
using Content.Shared.Preferences;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using static Content.Shared.Humanoid.HumanoidAppearanceState;
|
||||||
|
|
||||||
namespace Content.Shared.Humanoid.Prototypes;
|
namespace Content.Shared.Humanoid.Prototypes;
|
||||||
|
|
||||||
|
|||||||
200
Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs
Normal file
200
Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
using Content.Shared.Humanoid.Markings;
|
||||||
|
using Content.Shared.Humanoid.Prototypes;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Content.Shared.Humanoid;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HumanoidSystem. Primarily deals with the appearance and visual data
|
||||||
|
/// of a humanoid entity. HumanoidVisualizer is what deals with actually
|
||||||
|
/// organizing the sprites and setting up the sprite component's layers.
|
||||||
|
///
|
||||||
|
/// This is a shared system, because while it is server authoritative,
|
||||||
|
/// you still need a local copy so that players can set up their
|
||||||
|
/// characters.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class SharedHumanoidAppearanceSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
[Dependency] private readonly MarkingManager _markingManager = default!;
|
||||||
|
|
||||||
|
public const string DefaultSpecies = "Human";
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<HumanoidAppearanceComponent, ComponentGetState>(OnGetState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetState(EntityUid uid, HumanoidAppearanceComponent component, ref ComponentGetState args)
|
||||||
|
{
|
||||||
|
args.State = new HumanoidAppearanceState(component.MarkingSet,
|
||||||
|
component.PermanentlyHidden,
|
||||||
|
component.HiddenLayers,
|
||||||
|
component.CustomBaseLayers,
|
||||||
|
component.Sex,
|
||||||
|
component.Gender,
|
||||||
|
component.Age,
|
||||||
|
component.Species,
|
||||||
|
component.SkinColor,
|
||||||
|
component.EyeColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Toggles a humanoid's sprite layer visibility.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">Humanoid mob's UID</param>
|
||||||
|
/// <param name="layer">Layer to toggle visibility for</param>
|
||||||
|
/// <param name="humanoid">Humanoid component of the entity</param>
|
||||||
|
public void SetLayerVisibility(EntityUid uid,
|
||||||
|
HumanoidVisualLayers layer,
|
||||||
|
bool visible,
|
||||||
|
bool permanent = false,
|
||||||
|
HumanoidAppearanceComponent? humanoid = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref humanoid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var dirty = false;
|
||||||
|
SetLayerVisibility(uid, humanoid, layer, visible, permanent, ref dirty);
|
||||||
|
if (dirty)
|
||||||
|
Dirty(humanoid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the visibility for multiple layers at once on a humanoid's sprite.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">Humanoid mob's UID</param>
|
||||||
|
/// <param name="layers">An enumerable of all sprite layers that are going to have their visibility set</param>
|
||||||
|
/// <param name="visible">The visibility state of the layers given</param>
|
||||||
|
/// <param name="permanent">If this is a permanent change, or temporary. Permanent layers are stored in their own hash set.</param>
|
||||||
|
/// <param name="humanoid">Humanoid component of the entity</param>
|
||||||
|
public void SetLayersVisibility(EntityUid uid, IEnumerable<HumanoidVisualLayers> layers, bool visible, bool permanent = false,
|
||||||
|
HumanoidAppearanceComponent? humanoid = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref humanoid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var dirty = false;
|
||||||
|
|
||||||
|
foreach (var layer in layers)
|
||||||
|
{
|
||||||
|
SetLayerVisibility(uid, humanoid, layer, visible, permanent, ref dirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirty)
|
||||||
|
Dirty(humanoid);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void SetLayerVisibility(
|
||||||
|
EntityUid uid,
|
||||||
|
HumanoidAppearanceComponent humanoid,
|
||||||
|
HumanoidVisualLayers layer,
|
||||||
|
bool visible,
|
||||||
|
bool permanent,
|
||||||
|
ref bool dirty)
|
||||||
|
{
|
||||||
|
if (visible)
|
||||||
|
{
|
||||||
|
if (permanent)
|
||||||
|
dirty |= humanoid.PermanentlyHidden.Remove(layer);
|
||||||
|
|
||||||
|
dirty |= humanoid.HiddenLayers.Remove(layer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (permanent)
|
||||||
|
dirty |= humanoid.PermanentlyHidden.Add(layer);
|
||||||
|
|
||||||
|
dirty |= humanoid.HiddenLayers.Add(layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set a humanoid mob's species. This will change their base sprites, as well as their current
|
||||||
|
/// set of markings to fit against the mob's new species.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">The humanoid mob's UID.</param>
|
||||||
|
/// <param name="species">The species to set the mob to. Will return if the species prototype was invalid.</param>
|
||||||
|
/// <param name="sync">Whether to immediately synchronize this to the humanoid mob, or not.</param>
|
||||||
|
/// <param name="humanoid">Humanoid component of the entity</param>
|
||||||
|
public void SetSpecies(EntityUid uid, string species, bool sync = true, HumanoidAppearanceComponent? humanoid = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref humanoid) || !_prototypeManager.TryIndex<SpeciesPrototype>(species, out var prototype))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
humanoid.Species = species;
|
||||||
|
humanoid.MarkingSet.FilterSpecies(species, _markingManager);
|
||||||
|
var oldMarkings = humanoid.MarkingSet.GetForwardEnumerator().ToList();
|
||||||
|
humanoid.MarkingSet = new(oldMarkings, prototype.MarkingPoints, _markingManager, _prototypeManager);
|
||||||
|
|
||||||
|
if (sync)
|
||||||
|
Dirty(humanoid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the skin color of this humanoid mob. Will only affect base layers that are not custom,
|
||||||
|
/// custom base layers should use <see cref="SetBaseLayerColor"/> instead.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">The humanoid mob's UID.</param>
|
||||||
|
/// <param name="skinColor">Skin color to set on the humanoid mob.</param>
|
||||||
|
/// <param name="sync">Whether to synchronize this to the humanoid mob, or not.</param>
|
||||||
|
/// <param name="humanoid">Humanoid component of the entity</param>
|
||||||
|
public virtual void SetSkinColor(EntityUid uid, Color skinColor, bool sync = true, HumanoidAppearanceComponent? humanoid = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref humanoid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
humanoid.SkinColor = skinColor;
|
||||||
|
|
||||||
|
if (sync)
|
||||||
|
Dirty(humanoid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the base layer ID of this humanoid mob. A humanoid mob's 'base layer' is
|
||||||
|
/// the skin sprite that is applied to the mob's sprite upon appearance refresh.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">The humanoid mob's UID.</param>
|
||||||
|
/// <param name="layer">The layer to target on this humanoid mob.</param>
|
||||||
|
/// <param name="id">The ID of the sprite to use. See <see cref="HumanoidSpeciesSpriteLayer"/>.</param>
|
||||||
|
/// <param name="sync">Whether to synchronize this to the humanoid mob, or not.</param>
|
||||||
|
/// <param name="humanoid">Humanoid component of the entity</param>
|
||||||
|
public void SetBaseLayerId(EntityUid uid, HumanoidVisualLayers layer, string id, bool sync = true,
|
||||||
|
HumanoidAppearanceComponent? humanoid = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref humanoid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (humanoid.CustomBaseLayers.TryGetValue(layer, out var info))
|
||||||
|
humanoid.CustomBaseLayers[layer] = info with { ID = id };
|
||||||
|
else
|
||||||
|
humanoid.CustomBaseLayers[layer] = new(id);
|
||||||
|
|
||||||
|
if (sync)
|
||||||
|
Dirty(humanoid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the color of this humanoid mob's base layer. See <see cref="SetBaseLayerId"/> for a
|
||||||
|
/// description of how base layers work.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">The humanoid mob's UID.</param>
|
||||||
|
/// <param name="layer">The layer to target on this humanoid mob.</param>
|
||||||
|
/// <param name="color">The color to set this base layer to.</param>
|
||||||
|
public void SetBaseLayerColor(EntityUid uid, HumanoidVisualLayers layer, Color? color, bool sync = true, HumanoidAppearanceComponent? humanoid = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref humanoid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
humanoid.CustomBaseLayers[layer] = humanoid.CustomBaseLayers[layer] with { Color = color };
|
||||||
|
|
||||||
|
if (sync)
|
||||||
|
Dirty(humanoid);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using Content.Shared.Humanoid.Markings;
|
using Content.Shared.Humanoid.Markings;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
using static Content.Shared.Humanoid.HumanoidAppearanceState;
|
||||||
|
|
||||||
namespace Content.Shared.Humanoid;
|
namespace Content.Shared.Humanoid;
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ public sealed class HumanoidMarkingModifierBaseLayersSetMessage : BoundUserInter
|
|||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class HumanoidMarkingModifierState : BoundUserInterfaceState
|
public sealed class HumanoidMarkingModifierState : BoundUserInterfaceState
|
||||||
{
|
{
|
||||||
|
// TODO just use the component state, remove the BUI state altogether.
|
||||||
public HumanoidMarkingModifierState(MarkingSet markingSet, string species, Color skinColor, Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> customBaseLayers)
|
public HumanoidMarkingModifierState(MarkingSet markingSet, string species, Color skinColor, Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> customBaseLayers)
|
||||||
{
|
{
|
||||||
MarkingSet = markingSet;
|
MarkingSet = markingSet;
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
using Content.Shared.Humanoid.Markings;
|
|
||||||
using Content.Shared.Humanoid.Prototypes;
|
|
||||||
using Content.Shared.Preferences;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Shared.Humanoid;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// HumanoidSystem. Primarily deals with the appearance and visual data
|
|
||||||
/// of a humanoid entity. HumanoidVisualizer is what deals with actually
|
|
||||||
/// organizing the sprites and setting up the sprite component's layers.
|
|
||||||
///
|
|
||||||
/// This is a shared system, because while it is server authoritative,
|
|
||||||
/// you still need a local copy so that players can set up their
|
|
||||||
/// characters.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class SharedHumanoidSystem : EntitySystem
|
|
||||||
{
|
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
|
||||||
|
|
||||||
public const string DefaultSpecies = "Human";
|
|
||||||
|
|
||||||
public void SetAppearance(EntityUid uid,
|
|
||||||
string species,
|
|
||||||
Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo> customBaseLayer,
|
|
||||||
Color skinColor,
|
|
||||||
Sex sex,
|
|
||||||
List<HumanoidVisualLayers> visLayers,
|
|
||||||
List<Marking> markings)
|
|
||||||
{
|
|
||||||
var data = new HumanoidVisualizerData(species, customBaseLayer, skinColor, sex, visLayers, markings);
|
|
||||||
|
|
||||||
// This should raise a HumanoidAppearanceUpdateEvent, but that requires this component to be made networked and
|
|
||||||
// I cbf doing that atm.
|
|
||||||
_appearance.SetData(uid, HumanoidVisualizerKey.Key, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -99,7 +99,7 @@ namespace Content.Shared.Preferences
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the default humanoid character profile, using internal constant values.
|
/// Get the default humanoid character profile, using internal constant values.
|
||||||
/// Defaults to <see cref="SharedHumanoidSystem.DefaultSpecies"/> for the species.
|
/// Defaults to <see cref="SharedHumanoidAppearanceSystem.DefaultSpecies"/> for the species.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static HumanoidCharacterProfile Default()
|
public static HumanoidCharacterProfile Default()
|
||||||
@@ -107,7 +107,7 @@ namespace Content.Shared.Preferences
|
|||||||
return new(
|
return new(
|
||||||
"John Doe",
|
"John Doe",
|
||||||
"",
|
"",
|
||||||
SharedHumanoidSystem.DefaultSpecies,
|
SharedHumanoidAppearanceSystem.DefaultSpecies,
|
||||||
18,
|
18,
|
||||||
Sex.Male,
|
Sex.Male,
|
||||||
Gender.Male,
|
Gender.Male,
|
||||||
@@ -126,9 +126,9 @@ namespace Content.Shared.Preferences
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Return a default character profile, based on species.
|
/// Return a default character profile, based on species.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="species">The species to use in this default profile. The default species is <see cref="SharedHumanoidSystem.DefaultSpecies"/>.</param>
|
/// <param name="species">The species to use in this default profile. The default species is <see cref="SharedHumanoidAppearanceSystem.DefaultSpecies"/>.</param>
|
||||||
/// <returns>Humanoid character profile with default settings.</returns>
|
/// <returns>Humanoid character profile with default settings.</returns>
|
||||||
public static HumanoidCharacterProfile DefaultWithSpecies(string species = SharedHumanoidSystem.DefaultSpecies)
|
public static HumanoidCharacterProfile DefaultWithSpecies(string species = SharedHumanoidAppearanceSystem.DefaultSpecies)
|
||||||
{
|
{
|
||||||
return new(
|
return new(
|
||||||
"John Doe",
|
"John Doe",
|
||||||
@@ -164,7 +164,7 @@ namespace Content.Shared.Preferences
|
|||||||
return RandomWithSpecies(species);
|
return RandomWithSpecies(species);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HumanoidCharacterProfile RandomWithSpecies(string species = SharedHumanoidSystem.DefaultSpecies)
|
public static HumanoidCharacterProfile RandomWithSpecies(string species = SharedHumanoidAppearanceSystem.DefaultSpecies)
|
||||||
{
|
{
|
||||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
var random = IoCManager.Resolve<IRobustRandom>();
|
var random = IoCManager.Resolve<IRobustRandom>();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Content.Shared.Humanoid;
|
|||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
|
using static Content.Shared.Humanoid.HumanoidAppearanceState;
|
||||||
|
|
||||||
namespace Content.Shared.Zombies
|
namespace Content.Shared.Zombies
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
- MobMask
|
- MobMask
|
||||||
layer:
|
layer:
|
||||||
- MachineLayer
|
- MachineLayer
|
||||||
- type: Humanoid
|
- type: HumanoidAppearance
|
||||||
- type: AnimationPlayer
|
- type: AnimationPlayer
|
||||||
- type: MeleeWeapon
|
- type: MeleeWeapon
|
||||||
hidden: true
|
hidden: true
|
||||||
|
|||||||
@@ -173,7 +173,7 @@
|
|||||||
heatDamage:
|
heatDamage:
|
||||||
types:
|
types:
|
||||||
Heat: 0.1 #per second, scales with temperature & other constants
|
Heat: 0.1 #per second, scales with temperature & other constants
|
||||||
- type: Humanoid
|
- type: HumanoidAppearance
|
||||||
species: Human
|
species: Human
|
||||||
- type: Body
|
- type: Body
|
||||||
prototype: Human
|
prototype: Human
|
||||||
@@ -364,7 +364,7 @@
|
|||||||
- MobMask
|
- MobMask
|
||||||
layer:
|
layer:
|
||||||
- MobLayer
|
- MobLayer
|
||||||
- type: Humanoid
|
- type: HumanoidAppearance
|
||||||
species: Human
|
species: Human
|
||||||
- type: Body
|
- type: Body
|
||||||
prototype: Human
|
prototype: Human
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
id: BaseMobDiona
|
id: BaseMobDiona
|
||||||
abstract: true
|
abstract: true
|
||||||
components:
|
components:
|
||||||
- type: Humanoid
|
- type: HumanoidAppearance
|
||||||
species: Diona
|
species: Diona
|
||||||
- type: Hunger
|
- type: Hunger
|
||||||
- type: Thirst
|
- type: Thirst
|
||||||
@@ -61,5 +61,5 @@
|
|||||||
components:
|
components:
|
||||||
- type: Inventory
|
- type: Inventory
|
||||||
templateId: diona
|
templateId: diona
|
||||||
- type: Humanoid
|
- type: HumanoidAppearance
|
||||||
species: Diona
|
species: Diona
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
id: BaseMobReptilian
|
id: BaseMobReptilian
|
||||||
abstract: true
|
abstract: true
|
||||||
components:
|
components:
|
||||||
- type: Humanoid
|
- type: HumanoidAppearance
|
||||||
species: Reptilian
|
species: Reptilian
|
||||||
- type: Hunger
|
- type: Hunger
|
||||||
- type: Thirst
|
- type: Thirst
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
noSpawn: true
|
noSpawn: true
|
||||||
description: A dummy reptilian meant to be used in character setup.
|
description: A dummy reptilian meant to be used in character setup.
|
||||||
components:
|
components:
|
||||||
- type: Humanoid
|
- type: HumanoidAppearance
|
||||||
species: Reptilian
|
species: Reptilian
|
||||||
|
|
||||||
#Weh
|
#Weh
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
id: BaseMobSkeletonPerson
|
id: BaseMobSkeletonPerson
|
||||||
abstract: true
|
abstract: true
|
||||||
components:
|
components:
|
||||||
- type: Humanoid
|
- type: HumanoidAppearance
|
||||||
species: Skeleton
|
species: Skeleton
|
||||||
- type: Icon
|
- type: Icon
|
||||||
sprite: Mobs/Species/Skeleton/parts.rsi
|
sprite: Mobs/Species/Skeleton/parts.rsi
|
||||||
@@ -70,5 +70,5 @@
|
|||||||
noSpawn: true
|
noSpawn: true
|
||||||
description: A dummy skeleton meant to be used in character setup.
|
description: A dummy skeleton meant to be used in character setup.
|
||||||
components:
|
components:
|
||||||
- type: Humanoid
|
- type: HumanoidAppearance
|
||||||
species: Skeleton
|
species: Skeleton
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
- type: Body
|
- type: Body
|
||||||
prototype: Slime
|
prototype: Slime
|
||||||
requiredLegs: 2
|
requiredLegs: 2
|
||||||
- type: Humanoid
|
- type: HumanoidAppearance
|
||||||
species: SlimePerson
|
species: SlimePerson
|
||||||
- type: Speech
|
- type: Speech
|
||||||
speechSounds: Slime
|
speechSounds: Slime
|
||||||
@@ -81,5 +81,5 @@
|
|||||||
noSpawn: true
|
noSpawn: true
|
||||||
description: A dummy slime meant to be used in character setup.
|
description: A dummy slime meant to be used in character setup.
|
||||||
components:
|
components:
|
||||||
- type: Humanoid
|
- type: HumanoidAppearance
|
||||||
species: SlimePerson
|
species: SlimePerson
|
||||||
|
|||||||
@@ -90,7 +90,7 @@
|
|||||||
damageRecovery:
|
damageRecovery:
|
||||||
types:
|
types:
|
||||||
Asphyxiation: -1.0
|
Asphyxiation: -1.0
|
||||||
- type: Humanoid
|
- type: HumanoidAppearance
|
||||||
species: Vox
|
species: Vox
|
||||||
# canColorHair: false
|
# canColorHair: false
|
||||||
# canColorFacialHair: false
|
# canColorFacialHair: false
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
parent: MobHumanDummy
|
parent: MobHumanDummy
|
||||||
noSpawn: true
|
noSpawn: true
|
||||||
components:
|
components:
|
||||||
- type: Humanoid
|
- type: HumanoidAppearance
|
||||||
species: Vox
|
species: Vox
|
||||||
- type: Body
|
- type: Body
|
||||||
prototype: Vox
|
prototype: Vox
|
||||||
|
|||||||
Reference in New Issue
Block a user