using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.Administration.Managers; using Content.Shared.Administration; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Markings; using Content.Shared.Preferences; using Content.Shared.Verbs; using JetBrains.Annotations; using Robust.Shared.Containers; using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Shared.Body; public abstract partial class SharedVisualBodySystem { [Dependency] private readonly ISharedAdminManager _admin = default!; [Dependency] private readonly SharedUserInterfaceSystem _userInterface = default!; private void InitializeModifiers() { SubscribeLocalEvent>(OnGetVerbs); Subs.BuiEvents(HumanoidMarkingModifierKey.Key, subs => { subs.Event(OnModifiersOpened); subs.Event(OnSetModifiers); }); } private void OnGetVerbs(Entity ent, ref GetVerbsEvent args) { if (!_admin.HasAdminFlag(args.User, AdminFlags.Fun)) return; var user = args.User; args.Verbs.Add(new Verb { Text = "Modify markings", Category = VerbCategory.Tricks, Icon = new SpriteSpecifier.Rsi(new("/Textures/Mobs/Customization/reptilian_parts.rsi"), "tail_smooth"), Act = () => { _userInterface.OpenUi(ent.Owner, HumanoidMarkingModifierKey.Key, user); } }); } /// /// Copies the appearance of organs from one body to another /// /// The body whose organs to copy the appearance from /// The body whose organs to copy the appearance to [PublicAPI] public void CopyAppearanceFrom(Entity source, Entity target) { if (!Resolve(source, ref source.Comp) || !Resolve(target, ref target.Comp)) return; var sourceOrgans = _container.EnsureContainer(source, BodyComponent.ContainerID); foreach (var sourceOrgan in sourceOrgans.ContainedEntities) { var evt = new OrganCopyAppearanceEvent(sourceOrgan); RaiseLocalEvent(target, ref evt); } } /// /// Gathers all the markings-relevant data from this entity /// /// The entity to sample /// If set, only returns data concerning the given layers /// The profiles for the various organs /// The marking parameters for the various organs /// The markings that are applied to the entity public bool TryGatherMarkingsData(Entity ent, HashSet? filter, [NotNullWhen(true)] out Dictionary, OrganProfileData>? profiles, [NotNullWhen(true)] out Dictionary, OrganMarkingData>? markings, [NotNullWhen(true)] out Dictionary, Dictionary>>? applied) { if (!Resolve(ent, ref ent.Comp)) { profiles = null; markings = null; applied = null; return false; } profiles = new(); markings = new(); applied = new(); var organContainer = _container.EnsureContainer(ent, BodyComponent.ContainerID); foreach (var organ in organContainer.ContainedEntities) { if (!TryComp(organ, out var organComp) || organComp.Category is not { } category) continue; if (TryComp(organ, out var visualOrgan)) { profiles.TryAdd(category, visualOrgan.Profile); } if (TryComp(organ, out var visualOrganMarkings)) { markings.TryAdd(category, visualOrganMarkings.MarkingData); if (filter is not null) applied.TryAdd(category, visualOrganMarkings.Markings.Where(kvp => filter.Contains(kvp.Key)).ToDictionary()); else applied.TryAdd(category, visualOrganMarkings.Markings); } } return true; } private void OnModifiersOpened(Entity ent, ref BoundUIOpenedEvent args) { TryGatherMarkingsData(ent.AsNullable(), null, out var profiles, out var markings, out var applied); _userInterface.SetUiState(ent.Owner, HumanoidMarkingModifierKey.Key, new HumanoidMarkingModifierState(applied!, markings!, profiles!)); } private void OnSetModifiers(Entity ent, ref HumanoidMarkingModifierMarkingSetMessage args) { var markingsEvt = new ApplyOrganMarkingsEvent(args.Markings); RaiseLocalEvent(ent, ref markingsEvt); } /// /// Applies the given set of markings to the body. /// /// The body whose organs to apply markings to. /// A dictionary of organ categories to markings information. Organs not included in this dictionary will remain unaffected. [PublicAPI] public void ApplyMarkings(EntityUid ent, Dictionary, Dictionary>> markings) { var markingsEvt = new ApplyOrganMarkingsEvent(markings); RaiseLocalEvent(ent, ref markingsEvt); } private void ApplyAppearanceTo(Entity ent, HumanoidCharacterAppearance appearance, Sex sex) { if (!Resolve(ent, ref ent.Comp)) return; ApplyProfile(ent, new() { Sex = sex, SkinColor = appearance.SkinColor, EyeColor = appearance.EyeColor, }); var markingsEvt = new ApplyOrganMarkingsEvent(appearance.Markings); RaiseLocalEvent(ent, ref markingsEvt); } /// /// Applies the information contained with a to a visual body's appearance. /// This sets the profile data and markings of all organs contained within the profile. /// /// The body to apply the profile to /// The profile to apply [PublicAPI] public void ApplyProfileTo(Entity ent, HumanoidCharacterProfile profile) { ApplyAppearanceTo(ent, profile.Appearance, profile.Sex); } /// /// Applies profile data to all visual organs within the body. /// /// The body to apply the organ profile to /// The profile to apply [PublicAPI] public void ApplyProfile(EntityUid ent, OrganProfileData profile) { var profileEvt = new ApplyOrganProfileDataEvent(profile, null); RaiseLocalEvent(ent, ref profileEvt); } /// /// Applies profile data to the specified visual organs within the body. /// Organs not specified are left unchanged. /// /// The body to apply the organ profiles to. /// The profiles to apply. [PublicAPI] public void ApplyProfiles(EntityUid ent, Dictionary, OrganProfileData> profiles) { var profileEvt = new ApplyOrganProfileDataEvent(null, profiles); RaiseLocalEvent(ent, ref profileEvt); } }