using System.Diagnostics.CodeAnalysis; using Content.Shared.Access.Systems; using Content.Shared.CCVar; using Content.Shared.Examine; using Content.Shared.Localizations; using Content.Shared.Roles; using Content.Shared.Verbs; using Robust.Shared.Configuration; using Robust.Shared.Prototypes; using Robust.Shared.Utility; using System.Linq; using Robust.Shared.Containers; namespace Content.Shared.Contraband; /// /// This handles showing examine messages for contraband-marked items. /// public sealed partial class ContrabandSystem : EntitySystem { [Dependency] private IConfigurationManager _configuration = default!; [Dependency] private SharedIdCardSystem _id = default!; [Dependency] private ExamineSystemShared _examine = default!; private bool _contrabandExamineEnabled; private bool _contrabandExamineOnlyInHudEnabled; /// public override void Initialize() { SubscribeLocalEvent>(OnDetailedExamine); Subs.CVar(_configuration, CCVars.ContrabandExamine, SetContrabandExamine, true); Subs.CVar(_configuration, CCVars.ContrabandExamineOnlyInHUD, SetContrabandExamineOnlyInHUD, true); } public void CopyDetails(EntityUid uid, ContrabandComponent other, ContrabandComponent? contraband = null) { if (!Resolve(uid, ref contraband)) return; contraband.Severity = other.Severity; contraband.AllowedDepartments = other.AllowedDepartments; contraband.AllowedJobs = other.AllowedJobs; Dirty(uid, contraband); } private void OnDetailedExamine(EntityUid ent, ContrabandComponent component, ref GetVerbsEvent args) { if (!_contrabandExamineEnabled) return; // Checking if contraband is only shown in the HUD if (_contrabandExamineOnlyInHudEnabled) { var ev = new GetContrabandDetailsEvent(); RaiseLocalEvent(args.User, ref ev); if (!ev.CanShowContraband) return; } // CanAccess is not used here, because we want people to be able to examine legality in strip menu. if (!args.CanInteract) return; // two strings: // one, the actual informative 'this is restricted' // then, the 'you can/shouldn't carry this around' based on the ID the user is wearing var severity = ProtoMan.Index(component.Severity); // Corvax start: show the article before the allowed departments and jobs. var departmentExamineMessage = Loc.GetString(severity.ExamineText, ("type", ContrabandItemType.Item), ("color", severity.Color.ToHex())); if (severity.ShowDepartmentsAndJobs) { departmentExamineMessage += "\n" + GenerateDepartmentExamineMessage(component.AllowedDepartments, component.AllowedJobs, severity.Color); } // Corvax end // if it is fully restricted, you're department-less, or your department isn't in the allowed list, you cannot carry it. Otherwise, you can. var carryingMessage = Loc.GetString("contraband-examine-text-in-the-clear"); var iconTexture = "/Textures/Interface/VerbIcons/unlock-green.svg.192dpi.png"; if (IsContraband((ent, component), args.User, out _)) { carryingMessage = Loc.GetString("contraband-examine-text-avoid-carrying-around"); iconTexture = "/Textures/Interface/VerbIcons/lock-red.svg.192dpi.png"; } var examineMarkup = GetContrabandExamine(departmentExamineMessage, carryingMessage); _examine.AddHoverExamineVerb(args, component, Loc.GetString("contraband-examinable-verb-text"), examineMarkup.ToMarkup(), iconTexture); } /// /// Create an examine message from the given inputs! /// /// What departments this contraband is allowed in. /// What jobs this contraband is allowed in. /// The color of the text. /// The type of entity (item, reagent etc...) /// A localized string with the formatted message public string GenerateDepartmentExamineMessage(HashSet> allowedDepartments, HashSet> allowedJobs, Color color, ContrabandItemType itemType = ContrabandItemType.Item) { var localizedDepartments = allowedDepartments.Select(p => Loc.GetString("contraband-department-plural", ("department", Loc.GetString(ProtoMan.Index(p).Name)))); var jobs = allowedJobs.Select(p => ProtoMan.Index(p).LocalizedName).ToArray(); var localizedJobs = jobs.Select(p => Loc.GetString("contraband-job-plural", ("job", p))); //creating a combined list of jobs and departments for the restricted text var list = ContentLocalizationManager.FormatList(localizedDepartments.Concat(localizedJobs).ToList()); // department restricted text return Loc.GetString("contraband-examine-text-Restricted-department", ("departments", list), ("type", itemType), ("color", color.ToHex())); } private FormattedMessage GetContrabandExamine(string deptMessage, string carryMessage) { var msg = new FormattedMessage(); msg.AddMarkupOrThrow(deptMessage); msg.PushNewline(); msg.AddMarkupOrThrow(carryMessage); return msg; } private void SetContrabandExamine(bool val) { _contrabandExamineEnabled = val; } private void SetContrabandExamineOnlyInHUD(bool val) { _contrabandExamineOnlyInHudEnabled = val; } /// /// Determines if an item is contraband for a given player. If no player is provided, will just return if the item /// is contraband in general. /// /// The entity that we are checking for contraband. /// The player that we are checking if they are allowed to have this contraband. /// The contraband ProtoId if the item is contraband. /// public bool IsContraband(Entity contraband, EntityUid? player, [NotNullWhen(true)] out ProtoId? contraProtoId) { contraProtoId = null; if (!Resolve(contraband.Owner, ref contraband.Comp, false)) return false; contraProtoId = contraband.Comp.Severity; if (player == null) return true; List> departments = new(); var jobId = ""; if (_id.TryFindIdCard(player.Value, out var id)) { departments = id.Comp.JobDepartments; if (id.Comp.LocalizedJobTitle is not null) jobId = id.Comp.LocalizedJobTitle; } var jobs = contraband.Comp.AllowedJobs.Select(p => ProtoMan.Index(p).LocalizedName).ToArray(); // if it is fully restricted, you're department-less, or your department isn't in the allowed list, you cannot carry it. Otherwise, you can. if (departments.Intersect(contraband.Comp.AllowedDepartments).Any() || jobs.Contains(jobId)) return false; return true; } /// /// Checks if a storage has contraband. /// /// The entity that we are checking for contraband. /// The player that we are checking if they are allowed to have certain contraband. /// All contraband prototypes present in storage. public bool ContainerHasContraband(Entity contraband, EntityUid? player, out List> contrabandList) { contrabandList = []; if (!Resolve(contraband.Owner, ref contraband.Comp, false)) return false; foreach (var container in contraband.Comp.Containers.Values) { foreach (var ent in container.ContainedEntities) { if (IsContraband(ent, player, out var itemContraId)) contrabandList.Add((ProtoId)itemContraId); ContainerHasContraband(ent, player, out var itemContraList); contrabandList = contrabandList.Concat(itemContraList).ToList(); } } return contrabandList.Any(); } } /// /// The item type that the contraband text should follow in the description text. /// public enum ContrabandItemType { Item, Reagent }