Moves HumanoidAppearanceComponent to ECS (#4855)

* Moves HumanoidCharacterAppearance to ECS

* Makes HumanoidAppearanceSystem work over networks

* Makes HumanoidAppearanceSystem more efficient

* Cleans up the files

* Updates privacy on a couple of functions

* Fixes a few using references, renames a file

* Makes HumanoidAppearanceSystem more cleaner

* Fixes Magic Mirror

* Cleanup

* HumanoidAppearanceComponent now has a friend

SharedHumanoidAppearanceSystem is only allowed to act on this, now

* Fixes the Body-HumanoidAppearance ECS scaffolding

* a little cleanup never hurt anybody

* quick fix for magic mirror appearance access

* Replaces a networked event with a local one

This one was... causing bugs
This commit is contained in:
Flipp Syder
2021-10-16 15:28:02 -07:00
committed by GitHub
parent f69575e15a
commit 7dc6b95a10
17 changed files with 275 additions and 240 deletions

View File

@@ -1,85 +1,61 @@
using Content.Client.Cuffs.Components; using Content.Client.Cuffs.Components;
using Content.Shared.Body.Components; using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
using Content.Shared.CharacterAppearance; using Content.Shared.CharacterAppearance;
using Content.Shared.CharacterAppearance.Components; using Content.Shared.CharacterAppearance.Components;
using Content.Shared.CharacterAppearance.Systems;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Client.CharacterAppearance namespace Content.Client.CharacterAppearance.Systems
{ {
[RegisterComponent] public class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
public sealed class HumanoidAppearanceComponent : SharedHumanoidAppearanceComponent, IBodyPartAdded, IBodyPartRemoved
{ {
[Dependency] private readonly SpriteAccessoryManager _accessoryManager = default!; [Dependency] private readonly SpriteAccessoryManager _accessoryManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override HumanoidCharacterAppearance Appearance public override void Initialize()
{ {
get => base.Appearance; base.Initialize();
set
{ SubscribeLocalEvent<HumanoidAppearanceComponent, ChangedHumanoidAppearanceEvent>(UpdateLooks);
base.Appearance = value; SubscribeLocalEvent<HumanoidAppearanceBodyPartAddedEvent>(BodyPartAdded);
UpdateLooks(); SubscribeLocalEvent<HumanoidAppearanceBodyPartRemovedEvent>(BodyPartRemoved);
}
} }
public override Sex Sex private void UpdateLooks(EntityUid uid, HumanoidAppearanceComponent component, ChangedHumanoidAppearanceEvent args)
{ {
get => base.Sex; if(!EntityManager.TryGetComponent(uid, out SpriteComponent? sprite))
set
{
base.Sex = value;
UpdateLooks();
}
}
protected override void Startup()
{
base.Startup();
UpdateLooks();
}
private void UpdateLooks()
{
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
if (Appearance == null ||
!Owner.TryGetComponent(out SpriteComponent? sprite))
{
return; return;
}
if (Owner.TryGetComponent(out SharedBodyComponent? body)) if (EntityManager.TryGetComponent(uid, out SharedBodyComponent? body))
{ {
foreach (var (part, _) in body.Parts) foreach (var (part, _) in body.Parts)
{ {
if (!part.Owner.TryGetComponent(out SpriteComponent? partSprite)) if (part.Owner.TryGetComponent(out SpriteComponent? partSprite))
{ {
continue; partSprite!.Color = component.Appearance.SkinColor;
} }
partSprite.Color = Appearance.SkinColor;
} }
} }
sprite.LayerSetColor(HumanoidVisualLayers.Hair, sprite.LayerSetColor(HumanoidVisualLayers.Hair,
CanColorHair ? Appearance.HairColor : Color.White); component.CanColorHair ? component.Appearance.HairColor : Color.White);
sprite.LayerSetColor(HumanoidVisualLayers.FacialHair, sprite.LayerSetColor(HumanoidVisualLayers.FacialHair,
CanColorFacialHair ? Appearance.FacialHairColor : Color.White); component.CanColorFacialHair ? component.Appearance.FacialHairColor : Color.White);
sprite.LayerSetColor(HumanoidVisualLayers.Eyes, Appearance.EyeColor); sprite.LayerSetColor(HumanoidVisualLayers.Eyes, component.Appearance.EyeColor);
sprite.LayerSetState(HumanoidVisualLayers.Chest, Sex == Sex.Male ? "torso_m" : "torso_f"); sprite.LayerSetState(HumanoidVisualLayers.Chest, component.Sex == Sex.Male ? "torso_m" : "torso_f");
sprite.LayerSetState(HumanoidVisualLayers.Head, Sex == Sex.Male ? "head_m" : "head_f"); sprite.LayerSetState(HumanoidVisualLayers.Head, component.Sex == Sex.Male ? "head_m" : "head_f");
if (sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out _)) if (sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out _))
sprite.LayerSetVisible(HumanoidVisualLayers.StencilMask, Sex == Sex.Female); sprite.LayerSetVisible(HumanoidVisualLayers.StencilMask, component.Sex == Sex.Female);
if (Owner.TryGetComponent<CuffableComponent>(out var cuffed)) if (EntityManager.TryGetComponent<CuffableComponent>(uid, out var cuffed))
{ {
sprite.LayerSetVisible(HumanoidVisualLayers.Handcuffs, !cuffed.CanStillInteract); sprite.LayerSetVisible(HumanoidVisualLayers.Handcuffs, !cuffed.CanStillInteract);
} }
@@ -88,16 +64,16 @@ namespace Content.Client.CharacterAppearance
sprite.LayerSetVisible(HumanoidVisualLayers.Handcuffs, false); sprite.LayerSetVisible(HumanoidVisualLayers.Handcuffs, false);
} }
var hairStyle = Appearance.HairStyleId; var hairStyle = component.Appearance.HairStyleId;
if (string.IsNullOrWhiteSpace(hairStyle) || if (string.IsNullOrWhiteSpace(hairStyle) ||
!_accessoryManager.IsValidAccessoryInCategory(hairStyle, CategoriesHair)) !_accessoryManager.IsValidAccessoryInCategory(hairStyle, component.CategoriesHair))
{ {
hairStyle = HairStyles.DefaultHairStyle; hairStyle = HairStyles.DefaultHairStyle;
} }
var facialHairStyle = Appearance.FacialHairStyleId; var facialHairStyle = component.Appearance.FacialHairStyleId;
if (string.IsNullOrWhiteSpace(facialHairStyle) || if (string.IsNullOrWhiteSpace(facialHairStyle) ||
!_accessoryManager.IsValidAccessoryInCategory(facialHairStyle, CategoriesFacialHair)) !_accessoryManager.IsValidAccessoryInCategory(facialHairStyle, component.CategoriesFacialHair))
{ {
facialHairStyle = HairStyles.DefaultFacialHairStyle; facialHairStyle = HairStyles.DefaultFacialHairStyle;
} }
@@ -109,37 +85,40 @@ namespace Content.Client.CharacterAppearance
sprite.LayerSetSprite(HumanoidVisualLayers.FacialHair, facialHairPrototype.Sprite); sprite.LayerSetSprite(HumanoidVisualLayers.FacialHair, facialHairPrototype.Sprite);
} }
public void BodyPartAdded(BodyPartAddedEventArgs args) // Scaffolding until Body is moved to ECS.
private void BodyPartAdded(HumanoidAppearanceBodyPartAddedEvent args)
{ {
if (!Owner.TryGetComponent(out SpriteComponent? sprite)) if(!EntityManager.TryGetEntity(args.Uid, out var owner)) return;
if (!owner.TryGetComponent(out SpriteComponent? sprite))
{ {
return; return;
} }
if (!args.Part.Owner.HasComponent<SpriteComponent>()) if (!args.Args.Part.Owner.HasComponent<SpriteComponent>())
{ {
return; return;
} }
var layers = args.Part.ToHumanoidLayers(); var layers = args.Args.Part.ToHumanoidLayers();
// TODO BODY Layer color, sprite and state // TODO BODY Layer color, sprite and state
foreach (var layer in layers) foreach (var layer in layers)
sprite.LayerSetVisible(layer, true); sprite.LayerSetVisible(layer, true);
} }
public void BodyPartRemoved(BodyPartRemovedEventArgs args) private void BodyPartRemoved(HumanoidAppearanceBodyPartRemovedEvent args)
{ {
if (!Owner.TryGetComponent(out SpriteComponent? sprite)) if(!EntityManager.TryGetEntity(args.Uid, out var owner)) return;
if (!owner.TryGetComponent(out SpriteComponent? sprite))
{ {
return; return;
} }
if (!args.Part.Owner.HasComponent<SpriteComponent>()) if (!args.Args.Part.Owner.HasComponent<SpriteComponent>())
{ {
return; return;
} }
var layers = args.Part.ToHumanoidLayers(); var layers = args.Args.Part.ToHumanoidLayers();
// TODO BODY Layer color, sprite and state // TODO BODY Layer color, sprite and state
foreach (var layer in layers) foreach (var layer in layers)
sprite.LayerSetVisible(layer, false); sprite.LayerSetVisible(layer, false);

View File

@@ -1,8 +1,8 @@
using System.Linq; using System.Linq;
using Content.Client.CharacterAppearance;
using Content.Client.HUD.UI; using Content.Client.HUD.UI;
using Content.Client.Inventory; using Content.Client.Inventory;
using Content.Client.Preferences; using Content.Client.Preferences;
using Content.Shared.CharacterAppearance.Systems;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Preferences; using Content.Shared.Preferences;
using Content.Shared.Roles; using Content.Shared.Roles;
@@ -130,9 +130,7 @@ namespace Content.Client.Lobby.UI
else else
{ {
_summaryLabel.Text = selectedCharacter.Summary; _summaryLabel.Text = selectedCharacter.Summary;
var component = _previewDummy.GetComponent<HumanoidAppearanceComponent>(); EntitySystem.Get<SharedHumanoidAppearanceSystem>().UpdateFromProfile(_previewDummy.Uid, selectedCharacter);
component.UpdateFromProfile(selectedCharacter);
GiveDummyJobClothes(_previewDummy, selectedCharacter); GiveDummyJobClothes(_previewDummy, selectedCharacter);
} }
} }

View File

@@ -1,10 +1,10 @@
using System.Linq; using System.Linq;
using Content.Client.CharacterAppearance;
using Content.Client.Info; using Content.Client.Info;
using Content.Client.Lobby.UI; using Content.Client.Lobby.UI;
using Content.Client.Parallax; using Content.Client.Parallax;
using Content.Client.Resources; using Content.Client.Resources;
using Content.Client.Stylesheets; using Content.Client.Stylesheets;
using Content.Shared.CharacterAppearance.Systems;
using Content.Shared.Preferences; using Content.Shared.Preferences;
using Content.Shared.Roles; using Content.Shared.Roles;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
@@ -157,7 +157,7 @@ namespace Content.Client.Preferences.UI
Group = group; Group = group;
_previewDummy = entityManager.SpawnEntity("MobHumanDummy", MapCoordinates.Nullspace); _previewDummy = entityManager.SpawnEntity("MobHumanDummy", MapCoordinates.Nullspace);
_previewDummy.GetComponent<HumanoidAppearanceComponent>().UpdateFromProfile(profile); EntitySystem.Get<SharedHumanoidAppearanceSystem>().UpdateFromProfile(_previewDummy.Uid, profile);
var humanoid = profile as HumanoidCharacterProfile; var humanoid = profile as HumanoidCharacterProfile;
if (humanoid != null) if (humanoid != null)
{ {

View File

@@ -6,6 +6,7 @@ using Content.Client.Lobby.UI;
using Content.Client.Message; using Content.Client.Message;
using Content.Client.Stylesheets; using Content.Client.Stylesheets;
using Content.Shared.CharacterAppearance; using Content.Shared.CharacterAppearance;
using Content.Shared.CharacterAppearance.Systems;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Preferences; using Content.Shared.Preferences;
using Content.Shared.Roles; using Content.Shared.Roles;
@@ -596,7 +597,7 @@ namespace Content.Client.Preferences.UI
if (Profile is null) if (Profile is null)
return; return;
_previewDummy.GetComponent<HumanoidAppearanceComponent>().UpdateFromProfile(Profile); EntitySystem.Get<SharedHumanoidAppearanceSystem>().UpdateFromProfile(_previewDummy.Uid, Profile);
LobbyCharacterPreviewPanel.GiveDummyJobClothes(_previewDummy, Profile); LobbyCharacterPreviewPanel.GiveDummyJobClothes(_previewDummy, Profile);
} }

View File

@@ -1,9 +1,9 @@
using Content.Server.CharacterAppearance.Components;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.Actions.Behaviors; using Content.Shared.Actions.Behaviors;
using Content.Shared.Actions.Components; using Content.Shared.Actions.Components;
using Content.Shared.Audio; using Content.Shared.Audio;
using Content.Shared.CharacterAppearance; using Content.Shared.CharacterAppearance;
using Content.Shared.CharacterAppearance.Components;
using Content.Shared.Cooldown; using Content.Shared.Cooldown;
using Content.Shared.Sound; using Content.Shared.Sound;
using JetBrains.Annotations; using JetBrains.Annotations;

View File

@@ -1,52 +0,0 @@
using Content.Shared.Body.Components;
using Content.Shared.CharacterAppearance;
using Content.Shared.CharacterAppearance.Components;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
namespace Content.Server.CharacterAppearance.Components
{
[RegisterComponent]
public sealed class HumanoidAppearanceComponent : SharedHumanoidAppearanceComponent
{
public override HumanoidCharacterAppearance Appearance
{
get => base.Appearance;
set
{
base.Appearance = value;
if (Owner.TryGetComponent(out SharedBodyComponent? body))
{
foreach (var (part, _) in body.Parts)
{
if (!part.Owner.TryGetComponent(out SpriteComponent? sprite))
{
continue;
}
sprite.Color = value.SkinColor;
}
}
}
}
protected override void Startup()
{
base.Startup();
if (Appearance != null! && Owner.TryGetComponent(out SharedBodyComponent? body))
{
foreach (var (part, _) in body.Parts)
{
if (!part.Owner.TryGetComponent(out SpriteComponent? sprite))
{
continue;
}
sprite.Color = Appearance.SkinColor;
}
}
}
}
}

View File

@@ -1,3 +1,4 @@
using Content.Server.CharacterAppearance.Systems;
using Content.Server.UserInterface; using Content.Server.UserInterface;
using Content.Shared.CharacterAppearance; using Content.Shared.CharacterAppearance;
using Content.Shared.CharacterAppearance.Components; using Content.Shared.CharacterAppearance.Components;
@@ -89,6 +90,8 @@ namespace Content.Server.CharacterAppearance.Components
break; break;
} }
EntitySystem.Get<HumanoidAppearanceSystem>().ForceAppearanceUpdate(obj.Session.AttachedEntity.Uid);
} }
void IActivate.Activate(ActivateEventArgs eventArgs) void IActivate.Activate(ActivateEventArgs eventArgs)

View File

@@ -0,0 +1,32 @@
using Content.Shared.Body.Components;
using Content.Shared.CharacterAppearance.Components;
using Content.Shared.CharacterAppearance.Systems;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
namespace Content.Server.CharacterAppearance.Systems
{
public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<HumanoidAppearanceComponent, ChangedHumanoidAppearanceEvent>(UpdateSkinColor);
}
private void UpdateSkinColor(EntityUid uid, HumanoidAppearanceComponent component, ChangedHumanoidAppearanceEvent _)
{
if (EntityManager.TryGetComponent<SharedBodyComponent>(uid, out SharedBodyComponent? body))
{
foreach (var (part, _) in body.Parts)
{
if (part.Owner.TryGetComponent(out SpriteComponent? sprite))
{
sprite!.Color = component.Appearance.SkinColor;
}
}
}
}
}
}

View File

@@ -4,6 +4,7 @@ using Content.Server.EUI;
using Content.Server.Mind.Components; using Content.Server.Mind.Components;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.UserInterface; using Content.Server.UserInterface;
using Content.Shared.CharacterAppearance.Systems;
using Content.Shared.Cloning; using Content.Shared.Cloning;
using Content.Shared.MobState; using Content.Shared.MobState;
using Content.Shared.Popups; using Content.Shared.Popups;
@@ -136,7 +137,8 @@ namespace Content.Server.Cloning.Components
var mob = Owner.EntityManager.SpawnEntity("MobHuman", Owner.Transform.MapPosition); var mob = Owner.EntityManager.SpawnEntity("MobHuman", Owner.Transform.MapPosition);
mob.GetComponent<HumanoidAppearanceComponent>().UpdateFromProfile(dna.Profile);
EntitySystem.Get<SharedHumanoidAppearanceSystem>().UpdateFromProfile(mob.Uid, dna.Profile);
mob.Name = dna.Profile.Name; mob.Name = dna.Profile.Name;
var cloneMindReturn = mob.AddComponent<BeingClonedComponent>(); var cloneMindReturn = mob.AddComponent<BeingClonedComponent>();

View File

@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using Content.Server.Access.Components; using Content.Server.Access.Components;
using Content.Server.CharacterAppearance.Components;
using Content.Server.Ghost.Components; using Content.Server.Ghost.Components;
using Content.Server.Hands.Components; using Content.Server.Hands.Components;
using Content.Server.Inventory.Components; using Content.Server.Inventory.Components;
@@ -12,6 +11,7 @@ using Content.Server.Players;
using Content.Server.Roles; using Content.Server.Roles;
using Content.Server.Spawners.Components; using Content.Server.Spawners.Components;
using Content.Server.Speech.Components; using Content.Server.Speech.Components;
using Content.Shared.CharacterAppearance.Systems;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Ghost; using Content.Shared.Ghost;
using Content.Shared.Inventory; using Content.Shared.Inventory;
@@ -171,7 +171,7 @@ namespace Content.Server.GameTicking
if (profile != null) if (profile != null)
{ {
entity.GetComponent<HumanoidAppearanceComponent>().UpdateFromProfile(profile); EntitySystem.Get<SharedHumanoidAppearanceSystem>().UpdateFromProfile(entity.Uid, profile);
entity.Name = profile.Name; entity.Name = profile.Name;
} }

View File

@@ -8,6 +8,7 @@ using Content.Shared.Body.Part.Property;
using Content.Shared.Body.Preset; using Content.Shared.Body.Preset;
using Content.Shared.Body.Slot; using Content.Shared.Body.Slot;
using Content.Shared.Body.Template; using Content.Shared.Body.Template;
using Content.Shared.CharacterAppearance.Systems;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Prototypes;
using Content.Shared.Movement.Components; using Content.Shared.Movement.Components;
@@ -151,6 +152,7 @@ namespace Content.Shared.Body.Components
var argsAdded = new BodyPartAddedEventArgs(slot.Id, part); var argsAdded = new BodyPartAddedEventArgs(slot.Id, part);
EntitySystem.Get<SharedHumanoidAppearanceSystem>().BodyPartAdded(Owner.Uid, argsAdded);
foreach (var component in Owner.GetAllComponents<IBodyPartAdded>().ToArray()) foreach (var component in Owner.GetAllComponents<IBodyPartAdded>().ToArray())
{ {
component.BodyPartAdded(argsAdded); component.BodyPartAdded(argsAdded);
@@ -177,6 +179,8 @@ namespace Content.Shared.Body.Components
var args = new BodyPartRemovedEventArgs(slot.Id, part); var args = new BodyPartRemovedEventArgs(slot.Id, part);
EntitySystem.Get<SharedHumanoidAppearanceSystem>().BodyPartRemoved(Owner.Uid, args);
foreach (var component in Owner.GetAllComponents<IBodyPartRemoved>()) foreach (var component in Owner.GetAllComponents<IBodyPartRemoved>())
{ {
component.BodyPartRemoved(args); component.BodyPartRemoved(args);

View File

@@ -1,5 +1,6 @@
using System; using System;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.Body.Part namespace Content.Shared.Body.Part
{ {
@@ -17,6 +18,8 @@ namespace Content.Shared.Body.Part
void BodyPartAdded(BodyPartAddedEventArgs args); void BodyPartAdded(BodyPartAddedEventArgs args);
} }
[Serializable, NetSerializable]
public class BodyPartAddedEventArgs : EventArgs public class BodyPartAddedEventArgs : EventArgs
{ {
public BodyPartAddedEventArgs(string slot, SharedBodyPartComponent part) public BodyPartAddedEventArgs(string slot, SharedBodyPartComponent part)

View File

@@ -1,4 +1,5 @@
using System; using System;
using Robust.Shared.Serialization;
namespace Content.Shared.Body.Part namespace Content.Shared.Body.Part
{ {
@@ -16,6 +17,7 @@ namespace Content.Shared.Body.Part
void BodyPartRemoved(BodyPartRemovedEventArgs args); void BodyPartRemoved(BodyPartRemovedEventArgs args);
} }
[Serializable, NetSerializable]
public class BodyPartRemovedEventArgs : EventArgs public class BodyPartRemovedEventArgs : EventArgs
{ {
public BodyPartRemovedEventArgs(string slot, SharedBodyPartComponent part) public BodyPartRemovedEventArgs(string slot, SharedBodyPartComponent part)

View File

@@ -0,0 +1,63 @@
using System;
using Content.Shared.CharacterAppearance;
using Content.Shared.CharacterAppearance.Systems;
using Robust.Shared.Analyzers;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Shared.CharacterAppearance.Components
{
[RegisterComponent]
[Friend(typeof(SharedHumanoidAppearanceSystem), typeof(SharedMagicMirrorComponent))]
[NetworkedComponent]
public class HumanoidAppearanceComponent : Component
{
public override string Name => "HumanoidAppearance";
[ViewVariables]
public HumanoidCharacterAppearance Appearance { get; set; } = HumanoidCharacterAppearance.Default();
[ViewVariables(VVAccess.ReadWrite)]
public Sex Sex { get; set; } = default!;
[ViewVariables(VVAccess.ReadWrite)]
public Gender Gender { get; set; } = default!;
[DataField("categoriesHair")]
[ViewVariables]
public SpriteAccessoryCategories CategoriesHair { get; set; } = SpriteAccessoryCategories.HumanHair;
[DataField("categoriesFacialHair")]
[ViewVariables]
public SpriteAccessoryCategories CategoriesFacialHair { get; set; } = SpriteAccessoryCategories.HumanFacialHair;
[ViewVariables]
[DataField("canColorHair")]
public bool CanColorHair { get; set; } = true;
[ViewVariables]
[DataField("canColorFacialHair")]
public bool CanColorFacialHair { get; set; } = true;
}
[Serializable, NetSerializable]
public sealed class HumanoidAppearanceComponentState : ComponentState
{
public HumanoidCharacterAppearance Appearance { get; }
public Sex Sex { get; }
public Gender Gender { get; }
public HumanoidAppearanceComponentState(HumanoidCharacterAppearance appearance,
Sex sex,
Gender gender)
{
Appearance = appearance;
Sex = sex;
Gender = gender;
}
}
}

View File

@@ -1,119 +0,0 @@
using System;
using Content.Shared.Preferences;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Localization;
using Robust.Shared.GameStates;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Shared.CharacterAppearance.Components
{
[NetworkedComponent()]
public abstract class SharedHumanoidAppearanceComponent : Component
{
private HumanoidCharacterAppearance _appearance = HumanoidCharacterAppearance.Default();
private Sex _sex;
private Gender _gender;
public sealed override string Name => "HumanoidAppearance";
[DataField("categoriesHair")]
[ViewVariables]
public SpriteAccessoryCategories CategoriesHair { get; set; } = SpriteAccessoryCategories.HumanHair;
[DataField("categoriesFacialHair")]
[ViewVariables]
public SpriteAccessoryCategories CategoriesFacialHair { get; set; } = SpriteAccessoryCategories.HumanFacialHair;
[ViewVariables]
[DataField("canColorHair")]
public bool CanColorHair { get; set; } = true;
[ViewVariables]
[DataField("canColorFacialHair")]
public bool CanColorFacialHair { get; set; } = true;
[ViewVariables(VVAccess.ReadWrite)]
public virtual HumanoidCharacterAppearance Appearance
{
get => _appearance;
set
{
_appearance = value;
Dirty();
}
}
[ViewVariables(VVAccess.ReadWrite)]
public virtual Sex Sex
{
get => _sex;
set
{
_sex = value;
Dirty();
}
}
[ViewVariables(VVAccess.ReadWrite)]
public virtual Gender Gender
{
get => _gender;
set
{
_gender = value;
if (Owner.TryGetComponent(out GrammarComponent? g))
{
g.Gender = value;
}
Dirty();
}
}
public override ComponentState GetComponentState(ICommonSession player)
{
return new HumanoidAppearanceComponentState(Appearance, Sex, Gender);
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
base.HandleComponentState(curState, nextState);
if (curState is not HumanoidAppearanceComponentState cast)
return;
Appearance = cast.Appearance;
Sex = cast.Sex;
Gender = cast.Gender;
}
public void UpdateFromProfile(ICharacterProfile profile)
{
var humanoid = (HumanoidCharacterProfile) profile;
Appearance = (HumanoidCharacterAppearance) humanoid.CharacterAppearance;
Sex = humanoid.Sex;
Gender = humanoid.Gender;
}
[Serializable]
[NetSerializable]
private sealed class HumanoidAppearanceComponentState : ComponentState
{
public HumanoidAppearanceComponentState(HumanoidCharacterAppearance appearance, Sex sex, Gender gender)
{
Appearance = appearance;
Sex = sex;
Gender = gender;
}
public HumanoidCharacterAppearance Appearance { get; }
public Sex Sex { get; }
public Gender Gender { get; }
}
}
}

View File

@@ -0,0 +1,120 @@
using System;
using Content.Shared.Body.Part;
using Content.Shared.CharacterAppearance.Components;
using Content.Shared.Preferences;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.CharacterAppearance.Systems
{
public abstract class SharedHumanoidAppearanceSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<HumanoidAppearanceComponent, ComponentGetState>(OnAppearanceGetState);
SubscribeLocalEvent<HumanoidAppearanceComponent, ComponentHandleState>(OnAppearanceHandleState);
}
public void UpdateFromProfile(EntityUid uid, ICharacterProfile profile)
{
var humanoid = (HumanoidCharacterProfile) profile;
UpdateAppearance(uid, humanoid.Appearance, humanoid.Sex, humanoid.Gender);
}
// The magic mirror otherwise wouldn't work. (it directly modifies the component server-side)
public void ForceAppearanceUpdate(EntityUid uid, HumanoidAppearanceComponent? component = null)
{
if (!Resolve(uid, ref component)) return;
component.Dirty();
}
private void UpdateAppearance(EntityUid uid, HumanoidCharacterAppearance appearance, Sex sex, Gender gender, HumanoidAppearanceComponent? component = null)
{
if (!Resolve(uid, ref component)) return;
component.Appearance = appearance;
component.Sex = sex;
component.Gender = gender;
component.Dirty();
RaiseLocalEvent(uid, new ChangedHumanoidAppearanceEvent(appearance, sex, gender));
}
private void OnAppearanceGetState(EntityUid uid, HumanoidAppearanceComponent component, ref ComponentGetState args)
{
args.State = new HumanoidAppearanceComponentState(component.Appearance, component.Sex, component.Gender);
}
private void OnAppearanceHandleState(EntityUid uid, HumanoidAppearanceComponent component, ref ComponentHandleState args)
{
if (args.Current is not HumanoidAppearanceComponentState state)
return;
UpdateAppearance(uid, state.Appearance, state.Sex, state.Gender);
}
// Scaffolding until Body is moved to ECS.
public void BodyPartAdded(EntityUid uid, BodyPartAddedEventArgs args)
{
RaiseLocalEvent(new HumanoidAppearanceBodyPartAddedEvent(uid, args));
}
public void BodyPartRemoved(EntityUid uid, BodyPartRemovedEventArgs args)
{
RaiseLocalEvent(new HumanoidAppearanceBodyPartRemovedEvent(uid, args));
}
// Scaffolding until Body is moved to ECS.
[Serializable, NetSerializable]
public class HumanoidAppearanceBodyPartAddedEvent : EntityEventArgs
{
public EntityUid Uid { get; }
public BodyPartAddedEventArgs Args { get; }
public HumanoidAppearanceBodyPartAddedEvent(EntityUid uid, BodyPartAddedEventArgs args)
{
Uid = uid;
Args = args;
}
}
[Serializable, NetSerializable]
public class HumanoidAppearanceBodyPartRemovedEvent : EntityEventArgs
{
public EntityUid Uid { get; }
public BodyPartRemovedEventArgs Args { get; }
public HumanoidAppearanceBodyPartRemovedEvent(EntityUid uid, BodyPartRemovedEventArgs args)
{
Uid = uid;
Args = args;
}
}
[Serializable, NetSerializable]
public class ChangedHumanoidAppearanceEvent : EntityEventArgs
{
public HumanoidCharacterAppearance Appearance { get; }
public Sex Sex { get; }
public Gender Gender { get; }
public ChangedHumanoidAppearanceEvent(HumanoidCharacterProfile profile)
{
Appearance = profile.Appearance;
Sex = profile.Sex;
Gender = profile.Gender;
}
public ChangedHumanoidAppearanceEvent(HumanoidCharacterAppearance appearance, Sex sex, Gender gender)
{
Appearance = appearance;
Sex = sex;
Gender = gender;
}
}
}
}

View File

@@ -1,6 +1,5 @@
using Content.Shared.CharacterAppearance; using Content.Shared.CharacterAppearance;
namespace Content.Shared.Preferences namespace Content.Shared.Preferences
{ {
public interface ICharacterProfile public interface ICharacterProfile