using Content.Shared.Atmos.Components; using Content.Shared.Database; using Content.Shared.Examine; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Popups; using Content.Shared.SubFloor; using Content.Shared.Tools; using Content.Shared.Tools.Components; using Content.Shared.Tools.Systems; using Content.Shared.Verbs; using Robust.Shared.Prototypes; using System.Diagnostics.CodeAnalysis; namespace Content.Shared.Atmos.EntitySystems; /// /// The system responsible for checking and adjusting the connection layering of gas pipes /// public abstract partial class SharedAtmosPipeLayersSystem : EntitySystem { [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly IPrototypeManager _protoManager = default!; [Dependency] private readonly SharedToolSystem _tool = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnExamined); SubscribeLocalEvent>(OnGetVerb); SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnUseInHandEvent); SubscribeLocalEvent(OnSetNextPipeLayerCompleted); SubscribeLocalEvent(OnSettingPipeLayerCompleted); } private void OnExamined(Entity ent, ref ExaminedEvent args) { var layerName = GetPipeLayerName(ent.Comp.CurrentPipeLayer); args.PushMarkup(Loc.GetString("atmos-pipe-layers-component-current-layer", ("layerName", layerName))); } private void OnGetVerb(Entity ent, ref GetVerbsEvent args) { if (!args.CanAccess || !args.CanInteract || !args.CanComplexInteract) return; if (ent.Comp.NumberOfPipeLayers <= 1 || ent.Comp.PipeLayersLocked) return; if (!_protoManager.TryIndex(ent.Comp.Tool, out var toolProto)) return; var user = args.User; if (TryComp(ent, out var subFloorHide) && subFloorHide.IsUnderCover) { var v = new Verb { Priority = 1, Category = VerbCategory.Adjust, Text = Loc.GetString("atmos-pipe-layers-component-pipes-are-covered"), Disabled = true, Impact = LogImpact.Low, DoContactInteraction = true, }; args.Verbs.Add(v); } else if (!TryGetHeldTool(user, ent.Comp.Tool, out var tool)) { var v = new Verb { Priority = 1, Category = VerbCategory.Adjust, Text = Loc.GetString("atmos-pipe-layers-component-tool-missing", ("toolName", Loc.GetString(toolProto.ToolName).ToLower())), Disabled = true, Impact = LogImpact.Low, DoContactInteraction = true, }; args.Verbs.Add(v); } else { for (var i = 0; i < ent.Comp.NumberOfPipeLayers; i++) { var index = i; var layerName = GetPipeLayerName((AtmosPipeLayer)index); var label = Loc.GetString("atmos-pipe-layers-component-select-layer", ("layerName", layerName)); var v = new Verb { Priority = 1, Category = VerbCategory.Adjust, Text = label, Disabled = index == (int)ent.Comp.CurrentPipeLayer, Impact = LogImpact.Low, DoContactInteraction = true, Act = () => { _tool.UseTool(tool.Value, user, ent, ent.Comp.Delay, tool.Value.Comp.Qualities, new TrySettingPipeLayerCompletedEvent((AtmosPipeLayer)index)); } }; args.Verbs.Add(v); } } } private void OnInteractUsing(Entity ent, ref InteractUsingEvent args) { if (ent.Comp.NumberOfPipeLayers <= 1 || ent.Comp.PipeLayersLocked) return; if (!TryComp(args.Used, out var tool) || !_tool.HasQuality(args.Used, ent.Comp.Tool, tool)) return; if (TryComp(ent, out var subFloorHide) && subFloorHide.IsUnderCover) { _popup.PopupClient(Loc.GetString("atmos-pipe-layers-component-cannot-adjust-pipes"), ent, args.User); return; } _tool.UseTool(args.Used, args.User, ent, ent.Comp.Delay, tool.Qualities, new TrySetNextPipeLayerCompletedEvent()); } private void OnUseInHandEvent(Entity ent, ref UseInHandEvent args) { if (ent.Comp.NumberOfPipeLayers <= 1 || ent.Comp.PipeLayersLocked) return; if (!TryGetHeldTool(args.User, ent.Comp.Tool, out var tool)) { if (_protoManager.TryIndex(ent.Comp.Tool, out var toolProto)) { var toolName = Loc.GetString(toolProto.ToolName).ToLower(); var message = Loc.GetString("atmos-pipe-layers-component-tool-missing", ("toolName", toolName)); _popup.PopupClient(message, ent, args.User); } return; } _tool.UseTool(tool.Value, args.User, ent, ent.Comp.Delay, tool.Value.Comp.Qualities, new TrySetNextPipeLayerCompletedEvent()); } private void OnSetNextPipeLayerCompleted(Entity ent, ref TrySetNextPipeLayerCompletedEvent args) { if (args.Cancelled) return; SetNextPipeLayer(ent, args.User, args.Used); } private void OnSettingPipeLayerCompleted(Entity ent, ref TrySettingPipeLayerCompletedEvent args) { if (args.Cancelled) return; SetPipeLayer(ent, args.PipeLayer, args.User, args.Used); } /// /// Increments an entity's pipe layer by 1, wrapping around to 0 if the max pipe layer is reached /// /// The pipe entity /// The player entity who adjusting the pipe layer /// The tool used to adjust the pipe layer public void SetNextPipeLayer(Entity ent, EntityUid? user = null, EntityUid? used = null) { var newLayer = ((int)ent.Comp.CurrentPipeLayer + 1) % ent.Comp.NumberOfPipeLayers; SetPipeLayer(ent, (AtmosPipeLayer)newLayer, user, used); } /// /// Sets an entity's pipe layer to a specified value /// /// The pipe entity /// The new layer value /// The player entity who adjusting the pipe layer /// The tool used to adjust the pipe layer public virtual void SetPipeLayer(Entity ent, AtmosPipeLayer layer, EntityUid? user = null, EntityUid? used = null) { if (ent.Comp.PipeLayersLocked) return; ent.Comp.CurrentPipeLayer = (AtmosPipeLayer)Math.Clamp((int)layer, 0, ent.Comp.NumberOfPipeLayers - 1); Dirty(ent); if (TryComp(ent, out var appearance)) { if (ent.Comp.SpriteRsiPaths.TryGetValue(ent.Comp.CurrentPipeLayer, out var path)) _appearance.SetData(ent, AtmosPipeLayerVisuals.Sprite, path, appearance); if (ent.Comp.SpriteLayersRsiPaths.Count > 0) { var data = new Dictionary(); foreach (var (layerKey, rsiPaths) in ent.Comp.SpriteLayersRsiPaths) { if (rsiPaths.TryGetValue(ent.Comp.CurrentPipeLayer, out path)) data.TryAdd(layerKey, path); } _appearance.SetData(ent, AtmosPipeLayerVisuals.SpriteLayers, data, appearance); } } if (user != null) { var layerName = GetPipeLayerName(ent.Comp.CurrentPipeLayer); var message = Loc.GetString("atmos-pipe-layers-component-change-layer", ("layerName", layerName)); _popup.PopupClient(message, ent, user); } } /// /// Try to find an entity prototype associated with a specified . /// /// The with the alternative prototypes data. /// The atmos pipe layer associated with the entity prototype. /// The returned entity prototype. /// True if there was an entity prototype associated with the layer. public bool TryGetAlternativePrototype(AtmosPipeLayersComponent component, AtmosPipeLayer layer, out EntProtoId proto) { return component.AlternativePrototypes.TryGetValue(layer, out proto); } /// /// Checks a player entity's hands to see if they are holding a tool with a specified quality /// /// The player entity /// The tool quality being checked for /// A tool with the specified tool quality /// True if an appropriate tool was found private bool TryGetHeldTool(EntityUid user, ProtoId toolQuality, [NotNullWhen(true)] out Entity? heldTool) { heldTool = null; foreach (var heldItem in _hands.EnumerateHeld(user)) { if (TryComp(heldItem, out var tool) && _tool.HasQuality(heldItem, toolQuality, tool)) { heldTool = new Entity(heldItem, tool); return true; } } return false; } private string GetPipeLayerName(AtmosPipeLayer layer) { return Loc.GetString("atmos-pipe-layers-component-layer-" + layer.ToString().ToLower()); } }