diff --git a/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs b/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs new file mode 100644 index 0000000000..bb0f870f5a --- /dev/null +++ b/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs @@ -0,0 +1,64 @@ +using Content.Shared.Access.Systems; +using Robust.Client.GameObjects; + +namespace Content.Client.Access.UI +{ + /// + /// Initializes a and updates it when new server messages are received. + /// + public sealed class AgentIDCardBoundUserInterface : BoundUserInterface + { + private AgentIDCardWindow? _window; + + public AgentIDCardBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + _window = new AgentIDCardWindow(); + if (State != null) + UpdateState(State); + + _window.OpenCentered(); + + _window.OnClose += Close; + _window.OnNameEntered += OnNameChanged; + _window.OnJobEntered += OnJobChanged; + } + + private void OnNameChanged(string newName) + { + SendMessage(new AgentIDCardNameChangedMessage(newName)); + } + + private void OnJobChanged(string newJob) + { + SendMessage(new AgentIDCardJobChangedMessage(newJob)); + } + + /// + /// Update the UI state based on server-sent info + /// + /// + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + if (_window == null || state is not AgentIDCardBoundUserInterfaceState cast) + return; + + _window.SetCurrentName(cast.CurrentName); + _window.SetCurrentJob(cast.CurrentJob); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!disposing) return; + _window?.Dispose(); + } + } + +} diff --git a/Content.Client/Access/UI/AgentIDCardWindow.xaml b/Content.Client/Access/UI/AgentIDCardWindow.xaml new file mode 100644 index 0000000000..22bb3cb275 --- /dev/null +++ b/Content.Client/Access/UI/AgentIDCardWindow.xaml @@ -0,0 +1,10 @@ + + + + diff --git a/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs b/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs new file mode 100644 index 0000000000..9813a2946e --- /dev/null +++ b/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs @@ -0,0 +1,32 @@ +using Robust.Client.UserInterface.CustomControls; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.Access.UI +{ + [GenerateTypedNameReferences] + public sealed partial class AgentIDCardWindow : DefaultWindow + { + public event Action? OnNameEntered; + + public event Action? OnJobEntered; + + public AgentIDCardWindow() + { + RobustXamlLoader.Load(this); + + NameLineEdit.OnTextEntered += e => OnNameEntered?.Invoke(e.Text); + JobLineEdit.OnTextEntered += e => OnJobEntered?.Invoke(e.Text); + } + + public void SetCurrentName(string name) + { + NameLineEdit.Text = name; + } + + public void SetCurrentJob(string job) + { + JobLineEdit.Text = job; + } + } +} diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index 10778032cc..ec340c623e 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -64,6 +64,7 @@ namespace Content.Client.Entry "DiseaseSwab", "FloorTile", "RandomInsulation", + "AgentIDCard", "Electrified", "Electrocution", "Paper", diff --git a/Content.Server/Access/Components/AgentIDCardComponent.cs b/Content.Server/Access/Components/AgentIDCardComponent.cs new file mode 100644 index 0000000000..cedb5e49d9 --- /dev/null +++ b/Content.Server/Access/Components/AgentIDCardComponent.cs @@ -0,0 +1,9 @@ +using Content.Shared.Access.Systems; +using Content.Shared.PDA; + +namespace Content.Server.Access.Components +{ + [RegisterComponent] + public sealed class AgentIDCardComponent : Component + {} +} diff --git a/Content.Server/Access/Systems/AgentIDCardSystem.cs b/Content.Server/Access/Systems/AgentIDCardSystem.cs new file mode 100644 index 0000000000..e083f820d4 --- /dev/null +++ b/Content.Server/Access/Systems/AgentIDCardSystem.cs @@ -0,0 +1,66 @@ +using Content.Shared.Access.Components; +using Content.Server.Access.Components; +using Content.Shared.Access.Systems; +using Content.Shared.Interaction; +using Content.Server.Popups; +using Robust.Shared.Player; + +namespace Content.Server.Access.Systems +{ + public sealed class AgentIDCardSystem : SharedAgentIdCardSystem + { + [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly IdCardSystem _cardSystem = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnAfterInteract); + // BUI + SubscribeLocalEvent(OnNameChanged); + SubscribeLocalEvent (OnJobChanged); + + } + + private void OnAfterInteract(EntityUid uid, AgentIDCardComponent component, AfterInteractEvent args) + { + if (!TryComp(args.Target, out var targetAccess) || !TryComp(uid, out var targetIDCard) || args.Target == null) + return; + + if (!TryComp(uid, out var access) || !TryComp(uid, out var idCard)) + return; + + var beforeLength = access.Tags.Count; + access.Tags.UnionWith(targetAccess.Tags); + var addedLength = access.Tags.Count - beforeLength; + + if (addedLength == 0) + { + _popupSystem.PopupEntity(Loc.GetString("agent-id-no-new", ("card", args.Target)), args.Target.Value, Filter.Pvs(args.User)); + return; + } + else if (addedLength == 1) + { + _popupSystem.PopupEntity(Loc.GetString("agent-id-new-1", ("card", args.Target)), args.Target.Value, Filter.Pvs(args.User)); + return; + } + _popupSystem.PopupEntity(Loc.GetString("agent-id-new", ("number", addedLength), ("card", args.Target)), args.Target.Value, Filter.Pvs(args.User)); + } + + private void OnJobChanged(EntityUid uid, AgentIDCardComponent comp, AgentIDCardJobChangedMessage args) + { + if (!TryComp(uid, out var idCard)) + return; + + _cardSystem.TryChangeJobTitle(uid, args.Job, idCard); + } + + private void OnNameChanged(EntityUid uid, AgentIDCardComponent comp, AgentIDCardNameChangedMessage args) + { + if (!TryComp(uid, out var idCard)) + return; + + _cardSystem.TryChangeFullName(uid, args.Name, idCard); + } + } +} diff --git a/Content.Server/Bible/BibleSystem.cs b/Content.Server/Bible/BibleSystem.cs index 877057f111..26f94c67e5 100644 --- a/Content.Server/Bible/BibleSystem.cs +++ b/Content.Server/Bible/BibleSystem.cs @@ -1,20 +1,13 @@ -using System; -using Robust.Shared.GameObjects; using Content.Shared.Interaction; using Content.Shared.Inventory; using Content.Shared.MobState.Components; using Content.Shared.Damage; -using Content.Shared.Popups; using Content.Server.Cooldown; -using Content.Server.Inventory; -using Content.Server.Mind.Components; using Content.Server.Bible.Components; using Content.Server.Popups; -using Robust.Shared.IoC; using Robust.Shared.Random; using Robust.Shared.Audio; using Robust.Shared.Player; -using Robust.Shared.Localization; using Robust.Shared.Timing; @@ -70,7 +63,7 @@ namespace Content.Server.Bible if (_random.Prob(component.FailChance)) { var othersFailMessage = Loc.GetString("bible-heal-fail-others", ("user", args.User),("target", args.Target),("bible", uid)); - _popupSystem.PopupEntity(othersFailMessage, args.User, Filter.Pvs(args.User).RemoveWhereAttachedEntity(puid => puid == args.User)); + _popupSystem.PopupEntity(othersFailMessage, args.User, Filter.Pvs(args.User).RemoveWhereAttachedEntity(puid => puid == args.User)); var selfFailMessage = Loc.GetString("bible-heal-fail-self", ("target", args.Target),("bible", uid)); _popupSystem.PopupEntity(selfFailMessage, args.User, Filter.Entities(args.User)); diff --git a/Content.Shared/Access/Components/IdCardComponent.cs b/Content.Shared/Access/Components/IdCardComponent.cs index ccd2ef47e3..c114e0dba4 100644 --- a/Content.Shared/Access/Components/IdCardComponent.cs +++ b/Content.Shared/Access/Components/IdCardComponent.cs @@ -1,15 +1,12 @@ using Content.Shared.Access.Systems; using Content.Shared.PDA; -using Robust.Shared.Analyzers; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization.Manager.Attributes; namespace Content.Shared.Access.Components { // TODO BUI NETWORKING if ever clients can open their own BUI's (id card console, pda), then this data should be // networked. [RegisterComponent] - [Friend(typeof(SharedIdCardSystem), typeof(SharedPDASystem))] + [Friend(typeof(SharedIdCardSystem), typeof(SharedPDASystem), typeof(SharedAgentIdCardSystem))] public sealed class IdCardComponent : Component { [DataField("originalOwnerName")] diff --git a/Content.Shared/Access/SharedAgentIDCardSystem.cs b/Content.Shared/Access/SharedAgentIDCardSystem.cs new file mode 100644 index 0000000000..bb4a1fd1d1 --- /dev/null +++ b/Content.Shared/Access/SharedAgentIDCardSystem.cs @@ -0,0 +1,55 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Access.Systems +{ + public class SharedAgentIdCardSystem : EntitySystem + { + /// Just for friending for now + } + /// + /// Key representing which is currently open. + /// Useful when there are multiple UI for an object. Here it's future-proofing only. + /// + [Serializable, NetSerializable] + public enum AgentIDCardUiKey + { + Key, + } + + /// + /// Represents an state that can be sent to the client + /// + [Serializable, NetSerializable] + public sealed class AgentIDCardBoundUserInterfaceState : BoundUserInterfaceState + { + public string CurrentName { get; } + public string CurrentJob { get; } + + public AgentIDCardBoundUserInterfaceState(string currentName, string currentJob) + { + CurrentName = currentName; + CurrentJob = currentJob; + } + } + + [Serializable, NetSerializable] + public sealed class AgentIDCardNameChangedMessage : BoundUserInterfaceMessage + { + public string Name { get; } + + public AgentIDCardNameChangedMessage(string name) + { + Name = name; + } + } + + [Serializable, NetSerializable] + public sealed class AgentIDCardJobChangedMessage : BoundUserInterfaceMessage + { + public string Job { get; } + public AgentIDCardJobChangedMessage(string job) + { + Job = job; + } + } +} diff --git a/Resources/Locale/en-US/access/components/agent-id-card-component.ftl b/Resources/Locale/en-US/access/components/agent-id-card-component.ftl new file mode 100644 index 0000000000..2628813db5 --- /dev/null +++ b/Resources/Locale/en-US/access/components/agent-id-card-component.ftl @@ -0,0 +1,6 @@ +agent-id-no-new = Didn't gain any new accesses from {THE($card)}. +agent-id-new-1 = Gained one new access from {THE($card)}. +agent-id-new = Gained {$number} new accesses from {THE($card)}. +agent-id-card-current-name = Name: +agent-id-card-current-job = Job: +agent-id-menu-title = Agent ID Card diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index fd24c3a990..b12a613053 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -157,6 +157,15 @@ icon: /Textures/Objects/Tools/emag.rsi/icon.png price: 8 +- type: uplinkListing + id: UplinkAgentIDCard + category: Utility + itemId: AgentIDCard + listingName: Agent ID Card + description: A modified ID card that can copy accesses from other cards and change its name and job title at-will. + icon: Objects/Misc/id_cards.rsi/default.png + price: 3 + - type: uplinkListing id: UplinkHypopen category: Utility diff --git a/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml b/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml index ae7821399f..84a1f423dc 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml @@ -377,3 +377,16 @@ - state: idmusician - type: PresetIdCard job: Musician + +- type: entity + parent: AssistantIDCard + id: AgentIDCard + suffix: Agent + components: + - type: AgentIDCard + - type: ActivatableUI + key: enum.AgentIDCardUiKey.Key + - type: UserInterface + interfaces: + - key: enum.AgentIDCardUiKey.Key + type: AgentIDCardBoundUserInterface