using System.Linq; using Content.Client.Clothing; using Content.Client.Items.Systems; using Content.Shared.Clothing; using Content.Shared.Hands; using Content.Shared.Inventory; using Content.Shared.Item; using Content.Shared.Light.Components; using Content.Shared.Toggleable; using Robust.Client.GameObjects; using Robust.Shared.Utility; namespace Content.Client.Toggleable; /// /// Implements the behavior of by reacting to /// , for the sprite directly; for the /// in-hand visuals; and for the clothing visuals. /// /// public sealed class ToggleableVisualsSystem : VisualizerSystem { [Dependency] private readonly SharedItemSystem _item = default!; [Dependency] private readonly SharedPointLightSystem _pointLight = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnGetHeldVisuals, after: [typeof(ItemSystem)]); SubscribeLocalEvent(OnGetEquipmentVisuals, after: [typeof(ClientClothingSystem)]); } protected override void OnAppearanceChange(EntityUid uid, ToggleableVisualsComponent component, ref AppearanceChangeEvent args) { if (!AppearanceSystem.TryGetData(uid, ToggleableVisuals.Enabled, out var enabled, args.Component)) return; var modulateColor = AppearanceSystem.TryGetData(uid, ToggleableVisuals.Color, out var color, args.Component); // Update the item's sprite if (args.Sprite != null && component.SpriteLayer != null && SpriteSystem.LayerMapTryGet((uid, args.Sprite), component.SpriteLayer, out var layer, false)) { SpriteSystem.LayerSetVisible((uid, args.Sprite), layer, enabled); if (modulateColor) SpriteSystem.LayerSetColor((uid, args.Sprite), component.SpriteLayer, color); } // If there's a `ItemTogglePointLightComponent` that says to apply the color to attached lights, do so. if (TryComp(uid, out var toggleLights) && TryComp(uid, out PointLightComponent? light)) { DebugTools.Assert(!light.NetSyncEnabled, $"{typeof(ItemTogglePointLightComponent)} requires point lights without net-sync"); _pointLight.SetEnabled(uid, enabled, light); if (modulateColor && toggleLights.ToggleableVisualsColorModulatesLights) { _pointLight.SetColor(uid, color, light); } } // update clothing & in-hand visuals. _item.VisualsChanged(uid); } private void OnGetEquipmentVisuals(EntityUid uid, ToggleableVisualsComponent component, GetEquipmentVisualsEvent args) { if (!TryComp(uid, out AppearanceComponent? appearance) || !AppearanceSystem.TryGetData(uid, ToggleableVisuals.Enabled, out var enabled, appearance) || !enabled) return; if (!TryComp(args.Equipee, out InventoryComponent? inventory)) return; List? layers = null; // attempt to get species specific data if (inventory.SpeciesId != null) component.ClothingVisuals.TryGetValue($"{args.Slot}-{inventory.SpeciesId}", out layers); // No species specific data. Try to default to generic data. if (layers == null && !component.ClothingVisuals.TryGetValue(args.Slot, out layers)) return; var modulateColor = AppearanceSystem.TryGetData(uid, ToggleableVisuals.Color, out var color, appearance); var i = 0; foreach (var layer in layers) { var key = layer.MapKeys?.FirstOrDefault(); if (key == null) { key = i == 0 ? $"{args.Slot}-toggle" : $"{args.Slot}-toggle-{i}"; i++; } if (modulateColor) layer.Color = color; args.Layers.Add((key, layer)); } } private void OnGetHeldVisuals(EntityUid uid, ToggleableVisualsComponent component, GetInhandVisualsEvent args) { if (!TryComp(uid, out AppearanceComponent? appearance) || !AppearanceSystem.TryGetData(uid, ToggleableVisuals.Enabled, out var enabled, appearance) || !enabled) return; if (!component.InhandVisuals.TryGetValue(args.Location, out var layers)) return; var modulateColor = AppearanceSystem.TryGetData(uid, ToggleableVisuals.Color, out var color, appearance); var i = 0; var defaultKey = $"inhand-{args.Location.ToString().ToLowerInvariant()}-toggle"; foreach (var layer in layers) { var key = layer.MapKeys?.FirstOrDefault(); if (key == null) { key = i == 0 ? defaultKey : $"{defaultKey}-{i}"; i++; } if (modulateColor) layer.Color = color; args.Layers.Add((key, layer)); } } }