using System.Linq; using Content.Shared.Body; using Content.Shared.CCVar; using Content.Shared.Humanoid.Markings; using Content.Shared.Humanoid; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Shared.Configuration; using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Client.Body; public sealed class VisualBodySystem : SharedVisualBodySystem { [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly MarkingManager _marking = default!; [Dependency] private readonly SpriteSystem _sprite = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnOrganGotInserted); SubscribeLocalEvent(OnOrganGotRemoved); SubscribeLocalEvent(OnOrganState); SubscribeLocalEvent(OnMarkingsGotInserted); SubscribeLocalEvent(OnMarkingsGotRemoved); SubscribeLocalEvent(OnMarkingsState); SubscribeLocalEvent>(OnMarkingsChangedVisibility); Subs.CVar(_cfg, CCVars.AccessibilityClientCensorNudity, OnCensorshipChanged, true); Subs.CVar(_cfg, CCVars.AccessibilityServerCensorNudity, OnCensorshipChanged, true); } private void OnCensorshipChanged(bool value) { var query = AllEntityQuery(); while (query.MoveNext(out var ent, out var organComp, out var markingsComp)) { if (organComp.Body is not { } body) continue; RemoveMarkings((ent, markingsComp), body); ApplyMarkings((ent, markingsComp), body); } } private void OnOrganGotInserted(Entity ent, ref OrganGotInsertedEvent args) { ApplyVisual(ent, args.Target); } private void OnOrganGotRemoved(Entity ent, ref OrganGotRemovedEvent args) { RemoveVisual(ent, args.Target); } private void OnOrganState(Entity ent, ref AfterAutoHandleStateEvent args) { if (Comp(ent).Body is not { } body) return; ApplyVisual(ent, body); } private void ApplyVisual(Entity ent, EntityUid target) { if (!_sprite.LayerMapTryGet(target, ent.Comp.Layer, out var index, true)) return; _sprite.LayerSetData(target, index, ent.Comp.Data); } private void RemoveVisual(Entity ent, EntityUid target) { if (!_sprite.LayerMapTryGet(target, ent.Comp.Layer, out var index, true)) return; _sprite.LayerSetRsiState(target, index, RSI.StateId.Invalid); } private void OnMarkingsGotInserted(Entity ent, ref OrganGotInsertedEvent args) { ApplyMarkings(ent, args.Target); } private void OnMarkingsGotRemoved(Entity ent, ref OrganGotRemovedEvent args) { RemoveMarkings(ent, args.Target); } private void OnMarkingsState(Entity ent, ref AfterAutoHandleStateEvent args) { if (Comp(ent).Body is not { } body) return; RemoveMarkings(ent, body); ApplyMarkings(ent, body); } protected override void SetOrganColor(Entity ent, Color color) { base.SetOrganColor(ent, color); if (Comp(ent).Body is not { } body) return; ApplyVisual(ent, body); } protected override void SetOrganMarkings(Entity ent, Dictionary> markings) { base.SetOrganMarkings(ent, markings); if (Comp(ent).Body is not { } body) return; RemoveMarkings(ent, body); ApplyMarkings(ent, body); } protected override void SetOrganAppearance(Entity ent, PrototypeLayerData data) { base.SetOrganAppearance(ent, data); if (Comp(ent).Body is not { } body) return; ApplyVisual(ent, body); } private IEnumerable AllMarkings(Entity ent) { foreach (var markings in ent.Comp.Markings.Values) { foreach (var marking in markings) { yield return marking; } } var censorNudity = _cfg.GetCVar(CCVars.AccessibilityClientCensorNudity) || _cfg.GetCVar(CCVars.AccessibilityServerCensorNudity); if (!censorNudity) yield break; var group = _prototype.Index(ent.Comp.MarkingData.Group); foreach (var layer in ent.Comp.MarkingData.Layers) { if (!group.Limits.TryGetValue(layer, out var layerLimits)) continue; if (layerLimits.NudityDefault.Count < 1) continue; var markings = ent.Comp.Markings.GetValueOrDefault(layer) ?? []; if (markings.Any(marking => _marking.TryGetMarking(marking, out var proto) && proto.BodyPart == layer)) continue; foreach (var marking in layerLimits.NudityDefault) { yield return new(marking, 1); } } } private void ApplyMarkings(Entity ent, EntityUid target) { var applied = new List(); foreach (var marking in AllMarkings(ent)) { if (!_marking.TryGetMarking(marking, out var proto)) continue; if (!_sprite.LayerMapTryGet(target, proto.BodyPart, out var index, true)) continue; for (var i = 0; i < proto.Sprites.Count; i++) { var sprite = proto.Sprites[i]; DebugTools.Assert(sprite is SpriteSpecifier.Rsi); if (sprite is not SpriteSpecifier.Rsi rsi) continue; var layerId = $"{proto.ID}-{rsi.RsiState}"; if (!_sprite.LayerMapTryGet(target, layerId, out _, false)) { var layer = _sprite.AddLayer(target, sprite, index + i + 1); _sprite.LayerMapSet(target, layerId, layer); _sprite.LayerSetSprite(target, layerId, rsi); } if (marking.MarkingColors is not null && i < marking.MarkingColors.Count) _sprite.LayerSetColor(target, layerId, marking.MarkingColors[i]); else _sprite.LayerSetColor(target, layerId, Color.White); } applied.Add(marking); } ent.Comp.AppliedMarkings = applied; } private void RemoveMarkings(Entity ent, EntityUid target) { foreach (var marking in ent.Comp.AppliedMarkings) { if (!_marking.TryGetMarking(marking, out var proto)) continue; foreach (var sprite in proto.Sprites) { DebugTools.Assert(sprite is SpriteSpecifier.Rsi); if (sprite is not SpriteSpecifier.Rsi rsi) continue; var layerId = $"{proto.ID}-{rsi.RsiState}"; if (!_sprite.LayerMapTryGet(target, layerId, out var index, false)) continue; _sprite.LayerMapRemove(target, layerId); _sprite.RemoveLayer(target, index); } } } private void OnMarkingsChangedVisibility(Entity ent, ref BodyRelayedEvent args) { if (!ent.Comp.HideableLayers.Contains(args.Args.Layer)) return; foreach (var markings in ent.Comp.Markings.Values) { foreach (var marking in markings) { if (!_marking.TryGetMarking(marking, out var proto)) continue; if (proto.BodyPart != args.Args.Layer && !(ent.Comp.DependentHidingLayers.TryGetValue(args.Args.Layer, out var dependent) && dependent.Contains(proto.BodyPart))) continue; foreach (var sprite in proto.Sprites) { DebugTools.Assert(sprite is SpriteSpecifier.Rsi); if (sprite is not SpriteSpecifier.Rsi rsi) continue; var layerId = $"{proto.ID}-{rsi.RsiState}"; if (!_sprite.LayerMapTryGet(args.Body.Owner, layerId, out var index, true)) continue; _sprite.LayerSetVisible(args.Body.Owner, index, args.Args.Visible); } } } } }