Files
crystall-punk-14/Content.Server/UserInterface/ActivatableUISystem.cs

236 lines
8.0 KiB
C#
Raw Normal View History

2021-12-05 18:09:01 +01:00
using Content.Server.Administration.Managers;
using Content.Shared.ActionBlocker;
2023-08-25 18:50:46 +10:00
using Content.Shared.Ghost;
using Content.Shared.Hands;
2022-03-15 16:59:20 +13:00
using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
2022-03-13 01:33:23 +13:00
using Content.Shared.Interaction.Events;
using Content.Shared.Popups;
using Content.Shared.UserInterface;
using Content.Shared.Verbs;
using Robust.Server.GameObjects;
using Robust.Shared.Player;
2023-04-23 12:25:12 +10:00
namespace Content.Server.UserInterface;
public sealed partial class ActivatableUISystem : EntitySystem
{
2023-04-23 12:25:12 +10:00
[Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly ActionBlockerSystem _blockerSystem = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
2023-04-23 12:25:12 +10:00
public override void Initialize()
{
base.Initialize();
2023-04-23 12:25:12 +10:00
SubscribeLocalEvent<ActivatableUIComponent, ActivateInWorldEvent>(OnActivate);
SubscribeLocalEvent<ActivatableUIComponent, UseInHandEvent>(OnUseInHand);
Add door electronics access configuration menu (#17778) * Add door electronics configuration menu * Use file-scoped namespaces Signed-off-by: c4llv07e <kseandi@gmail.com> * Open door electronics configuration menu only with network configurator Signed-off-by: c4llv07e <kseandi@gmail.com> * Doors will now try to move their AccessReaderComponent to their door electronics when the map is initialized Signed-off-by: c4llv07e <kseandi@gmail.com> * Make the access list in the id card computer a separate control Signed-off-by: c4llv07e <kseandi@gmail.com> * Fix merge conflict Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove DoorElectronics tag Signed-off-by: c4llv07e <kseandi@gmail.com> * Integrate doors with #17927 Signed-off-by: c4llv07e <kseandi@gmail.com> * Move door electornics ui stuff to the right place Signed-off-by: c4llv07e <kseandi@gmail.com> * Some review fixes Signed-off-by: c4llv07e <kseandi@gmail.com> * More fixes Signed-off-by: c4llv07e <kseandi@gmail.com> * review fix Signed-off-by: c4llv07e <kseandi@gmail.com> * move all accesses from airlock prototypes to door electronics Signed-off-by: c4llv07e <kseandi@gmail.com> * rework door electronics config access list Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove Linq from the door electronics user interface * [WIP] Add EntityWhitelist to the activatable ui component Signed-off-by: c4llv07e <kseandi@gmail.com> * Better interaction system Signed-off-by: c4llv07e <kseandi@gmail.com> * Refactor Signed-off-by: c4llv07e <kseandi@gmail.com> * Fix some door electronics not working without AccessReaderComponent Signed-off-by: c4llv07e <kseandi@gmail.com> * Move AccessReaderComponent update code to the AccessReaderSystem Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove unnecesary newlines in the door access prototypes Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove unused variables in access level control Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove unnecessary method from the door electronics configuration menu Signed-off-by: c4llv07e <kseandi@gmail.com> * [WIP] change access type from string to ProtoId<AccessLevelPrototype> Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove unused methods Signed-off-by: c4llv07e <kseandi@gmail.com> * Newline fix Signed-off-by: c4llv07e <kseandi@gmail.com> * Restored to a functional state Signed-off-by: c4llv07e <kseandi@gmail.com> * Fix access configurator not working with door electronics AccessReaderComponent Signed-off-by: c4llv07e <kseandi@gmail.com> * Replace all string access fields with ProtoId Signed-off-by: c4llv07e <kseandi@gmail.com> * move access level control initialization into Populate method Signed-off-by: c4llv07e <kseandi@gmail.com> * Review --------- Signed-off-by: c4llv07e <kseandi@gmail.com> Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-04-01 09:06:13 +03:00
SubscribeLocalEvent<ActivatableUIComponent, InteractUsingEvent>(OnInteractUsing);
2023-04-23 12:25:12 +10:00
SubscribeLocalEvent<ActivatableUIComponent, HandDeselectedEvent>(OnHandDeselected);
SubscribeLocalEvent<ActivatableUIComponent, GotUnequippedHandEvent>((uid, aui, _) => CloseAll(uid, aui));
// *THIS IS A BLATANT WORKAROUND!* RATIONALE: Microwaves need it
SubscribeLocalEvent<ActivatableUIComponent, EntParentChangedMessage>(OnParentChanged);
SubscribeLocalEvent<ActivatableUIComponent, BoundUIClosedEvent>(OnUIClose);
SubscribeLocalEvent<BoundUserInterfaceMessageAttempt>(OnBoundInterfaceInteractAttempt);
2023-04-23 12:25:12 +10:00
SubscribeLocalEvent<ActivatableUIComponent, GetVerbsEvent<ActivationVerb>>(AddOpenUiVerb);
2023-09-11 21:20:46 +10:00
SubscribeLocalEvent<UserInterfaceComponent, OpenUiActionEvent>(OnActionPerform);
2023-04-23 12:25:12 +10:00
InitializePower();
}
2022-03-15 16:59:20 +13:00
2023-04-23 12:25:12 +10:00
private void OnBoundInterfaceInteractAttempt(BoundUserInterfaceMessageAttempt ev)
{
if (!TryComp(ev.Target, out ActivatableUIComponent? comp))
return;
2022-03-15 16:59:20 +13:00
2023-04-23 12:25:12 +10:00
if (!comp.RequireHands)
return;
2022-03-15 16:59:20 +13:00
2023-04-23 12:25:12 +10:00
if (!TryComp(ev.Sender.AttachedEntity, out HandsComponent? hands) || hands.Hands.Count == 0)
ev.Cancel();
}
2023-09-11 21:20:46 +10:00
private void OnActionPerform(EntityUid uid, UserInterfaceComponent component, OpenUiActionEvent args)
2023-04-23 12:25:12 +10:00
{
if (args.Handled || args.Key == null)
return;
2023-04-23 12:25:12 +10:00
if (!TryComp(args.Performer, out ActorComponent? actor))
return;
2023-04-23 12:25:12 +10:00
args.Handled = _uiSystem.TryToggleUi(uid, args.Key, actor.PlayerSession);
}
2023-04-23 12:25:12 +10:00
private void AddOpenUiVerb(EntityUid uid, ActivatableUIComponent component, GetVerbsEvent<ActivationVerb> args)
{
if (!args.CanAccess)
return;
2023-04-23 12:25:12 +10:00
if (component.RequireHands && args.Hands == null)
return;
2022-02-25 00:10:12 +13:00
2023-04-23 12:25:12 +10:00
if (component.InHandsOnly && args.Using != uid)
return;
2023-04-23 12:25:12 +10:00
if (!args.CanInteract && (!component.AllowSpectator || !HasComp<GhostComponent>(args.User)))
return;
2023-04-23 12:25:12 +10:00
ActivationVerb verb = new();
2023-10-11 02:17:59 -07:00
verb.Act = () => InteractUI(args.User, uid, component);
2023-04-23 12:25:12 +10:00
verb.Text = Loc.GetString(component.VerbText);
// TODO VERBS add "open UI" icon?
args.Verbs.Add(verb);
}
2023-04-23 12:25:12 +10:00
private void OnActivate(EntityUid uid, ActivatableUIComponent component, ActivateInWorldEvent args)
{
2023-10-11 02:17:59 -07:00
if (args.Handled)
return;
if (component.InHandsOnly)
return;
if (component.AllowedItems != null)
return;
2023-10-11 02:17:59 -07:00
args.Handled = InteractUI(args.User, uid, component);
2023-04-23 12:25:12 +10:00
}
2023-04-23 12:25:12 +10:00
private void OnUseInHand(EntityUid uid, ActivatableUIComponent component, UseInHandEvent args)
{
2023-10-11 02:17:59 -07:00
if (args.Handled)
return;
Add door electronics access configuration menu (#17778) * Add door electronics configuration menu * Use file-scoped namespaces Signed-off-by: c4llv07e <kseandi@gmail.com> * Open door electronics configuration menu only with network configurator Signed-off-by: c4llv07e <kseandi@gmail.com> * Doors will now try to move their AccessReaderComponent to their door electronics when the map is initialized Signed-off-by: c4llv07e <kseandi@gmail.com> * Make the access list in the id card computer a separate control Signed-off-by: c4llv07e <kseandi@gmail.com> * Fix merge conflict Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove DoorElectronics tag Signed-off-by: c4llv07e <kseandi@gmail.com> * Integrate doors with #17927 Signed-off-by: c4llv07e <kseandi@gmail.com> * Move door electornics ui stuff to the right place Signed-off-by: c4llv07e <kseandi@gmail.com> * Some review fixes Signed-off-by: c4llv07e <kseandi@gmail.com> * More fixes Signed-off-by: c4llv07e <kseandi@gmail.com> * review fix Signed-off-by: c4llv07e <kseandi@gmail.com> * move all accesses from airlock prototypes to door electronics Signed-off-by: c4llv07e <kseandi@gmail.com> * rework door electronics config access list Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove Linq from the door electronics user interface * [WIP] Add EntityWhitelist to the activatable ui component Signed-off-by: c4llv07e <kseandi@gmail.com> * Better interaction system Signed-off-by: c4llv07e <kseandi@gmail.com> * Refactor Signed-off-by: c4llv07e <kseandi@gmail.com> * Fix some door electronics not working without AccessReaderComponent Signed-off-by: c4llv07e <kseandi@gmail.com> * Move AccessReaderComponent update code to the AccessReaderSystem Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove unnecesary newlines in the door access prototypes Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove unused variables in access level control Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove unnecessary method from the door electronics configuration menu Signed-off-by: c4llv07e <kseandi@gmail.com> * [WIP] change access type from string to ProtoId<AccessLevelPrototype> Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove unused methods Signed-off-by: c4llv07e <kseandi@gmail.com> * Newline fix Signed-off-by: c4llv07e <kseandi@gmail.com> * Restored to a functional state Signed-off-by: c4llv07e <kseandi@gmail.com> * Fix access configurator not working with door electronics AccessReaderComponent Signed-off-by: c4llv07e <kseandi@gmail.com> * Replace all string access fields with ProtoId Signed-off-by: c4llv07e <kseandi@gmail.com> * move access level control initialization into Populate method Signed-off-by: c4llv07e <kseandi@gmail.com> * Review --------- Signed-off-by: c4llv07e <kseandi@gmail.com> Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-04-01 09:06:13 +03:00
if (component.RightClickOnly)
2023-10-11 02:17:59 -07:00
return;
if (component.AllowedItems != null)
return;
2023-10-11 02:17:59 -07:00
args.Handled = InteractUI(args.User, uid, component);
2023-04-23 12:25:12 +10:00
}
Add door electronics access configuration menu (#17778) * Add door electronics configuration menu * Use file-scoped namespaces Signed-off-by: c4llv07e <kseandi@gmail.com> * Open door electronics configuration menu only with network configurator Signed-off-by: c4llv07e <kseandi@gmail.com> * Doors will now try to move their AccessReaderComponent to their door electronics when the map is initialized Signed-off-by: c4llv07e <kseandi@gmail.com> * Make the access list in the id card computer a separate control Signed-off-by: c4llv07e <kseandi@gmail.com> * Fix merge conflict Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove DoorElectronics tag Signed-off-by: c4llv07e <kseandi@gmail.com> * Integrate doors with #17927 Signed-off-by: c4llv07e <kseandi@gmail.com> * Move door electornics ui stuff to the right place Signed-off-by: c4llv07e <kseandi@gmail.com> * Some review fixes Signed-off-by: c4llv07e <kseandi@gmail.com> * More fixes Signed-off-by: c4llv07e <kseandi@gmail.com> * review fix Signed-off-by: c4llv07e <kseandi@gmail.com> * move all accesses from airlock prototypes to door electronics Signed-off-by: c4llv07e <kseandi@gmail.com> * rework door electronics config access list Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove Linq from the door electronics user interface * [WIP] Add EntityWhitelist to the activatable ui component Signed-off-by: c4llv07e <kseandi@gmail.com> * Better interaction system Signed-off-by: c4llv07e <kseandi@gmail.com> * Refactor Signed-off-by: c4llv07e <kseandi@gmail.com> * Fix some door electronics not working without AccessReaderComponent Signed-off-by: c4llv07e <kseandi@gmail.com> * Move AccessReaderComponent update code to the AccessReaderSystem Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove unnecesary newlines in the door access prototypes Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove unused variables in access level control Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove unnecessary method from the door electronics configuration menu Signed-off-by: c4llv07e <kseandi@gmail.com> * [WIP] change access type from string to ProtoId<AccessLevelPrototype> Signed-off-by: c4llv07e <kseandi@gmail.com> * Remove unused methods Signed-off-by: c4llv07e <kseandi@gmail.com> * Newline fix Signed-off-by: c4llv07e <kseandi@gmail.com> * Restored to a functional state Signed-off-by: c4llv07e <kseandi@gmail.com> * Fix access configurator not working with door electronics AccessReaderComponent Signed-off-by: c4llv07e <kseandi@gmail.com> * Replace all string access fields with ProtoId Signed-off-by: c4llv07e <kseandi@gmail.com> * move access level control initialization into Populate method Signed-off-by: c4llv07e <kseandi@gmail.com> * Review --------- Signed-off-by: c4llv07e <kseandi@gmail.com> Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-04-01 09:06:13 +03:00
private void OnInteractUsing(EntityUid uid, ActivatableUIComponent component, InteractUsingEvent args)
{
if (args.Handled) return;
if (component.AllowedItems == null) return;
if (!component.AllowedItems.IsValid(args.Used, EntityManager)) return;
args.Handled = InteractUI(args.User, uid, component);
}
2023-04-23 12:25:12 +10:00
private void OnParentChanged(EntityUid uid, ActivatableUIComponent aui, ref EntParentChangedMessage args)
{
CloseAll(uid, aui);
}
2023-04-23 12:25:12 +10:00
private void OnUIClose(EntityUid uid, ActivatableUIComponent component, BoundUIClosedEvent args)
{
2023-10-11 02:17:59 -07:00
if (args.Session != component.CurrentSingleUser)
return;
if (!Equals(args.UiKey, component.Key))
return;
2023-04-23 12:25:12 +10:00
SetCurrentSingleUser(uid, null, component);
}
2023-10-11 02:17:59 -07:00
private bool InteractUI(EntityUid user, EntityUid uiEntity, ActivatableUIComponent aui)
2023-04-23 12:25:12 +10:00
{
if (!TryComp(user, out ActorComponent? actor))
2023-04-23 12:25:12 +10:00
return false;
if (aui.Key == null)
2023-04-23 12:25:12 +10:00
return false;
if (!_uiSystem.TryGetUi(uiEntity, aui.Key, out var ui))
2023-10-11 02:17:59 -07:00
return false;
if (ui.SubscribedSessions.Contains(actor.PlayerSession))
{
_uiSystem.CloseUi(ui, actor.PlayerSession);
return true;
}
if (!_blockerSystem.CanInteract(user, uiEntity) && (!aui.AllowSpectator || !HasComp<GhostComponent>(user)))
2023-10-11 02:17:59 -07:00
return false;
if (aui.RequireHands && !HasComp<HandsComponent>(user))
2023-10-11 02:17:59 -07:00
return false;
if (aui.AdminOnly && !_adminManager.IsAdmin(actor.PlayerSession))
2023-10-11 02:17:59 -07:00
return false;
2023-04-23 12:25:12 +10:00
if (aui.SingleUser && (aui.CurrentSingleUser != null) && (actor.PlayerSession != aui.CurrentSingleUser))
{
string message = Loc.GetString("machine-already-in-use", ("machine", uiEntity));
_popupSystem.PopupEntity(message, uiEntity, user);
2023-04-23 12:25:12 +10:00
// If we get here, supposedly, the object is in use.
// Check with BUI that it's ACTUALLY in use just in case.
// Since this could brick the object if it goes wrong.
2023-10-11 02:17:59 -07:00
if (ui.SubscribedSessions.Count != 0)
return false;
2023-04-23 12:25:12 +10:00
}
2023-04-23 12:25:12 +10:00
// If we've gotten this far, fire a cancellable event that indicates someone is about to activate this.
// This is so that stuff can require further conditions (like power).
var oae = new ActivatableUIOpenAttemptEvent(user);
2023-10-11 02:17:59 -07:00
var uae = new UserOpenActivatableUIAttemptEvent(user, uiEntity);
RaiseLocalEvent(user, uae);
RaiseLocalEvent(uiEntity, oae);
if (oae.Cancelled || uae.Cancelled)
return false;
2023-04-23 12:25:12 +10:00
// Give the UI an opportunity to prepare itself if it needs to do anything
// before opening
var bae = new BeforeActivatableUIOpenEvent(user);
2023-10-11 02:17:59 -07:00
RaiseLocalEvent(uiEntity, bae);
2023-10-11 02:17:59 -07:00
SetCurrentSingleUser(uiEntity, actor.PlayerSession, aui);
_uiSystem.OpenUi(ui, actor.PlayerSession);
2023-04-23 12:25:12 +10:00
//Let the component know a user opened it so it can do whatever it needs to do
var aae = new AfterActivatableUIOpenEvent(user, actor.PlayerSession);
2023-10-11 02:17:59 -07:00
RaiseLocalEvent(uiEntity, aae);
2023-04-23 12:25:12 +10:00
return true;
}
public void SetCurrentSingleUser(EntityUid uid, ICommonSession? v, ActivatableUIComponent? aui = null)
2023-04-23 12:25:12 +10:00
{
if (!Resolve(uid, ref aui))
return;
if (!aui.SingleUser)
return;
2023-04-23 12:25:12 +10:00
aui.CurrentSingleUser = v;
2023-10-11 02:17:59 -07:00
RaiseLocalEvent(uid, new ActivatableUIPlayerChangedEvent());
}
2023-04-23 12:25:12 +10:00
public void CloseAll(EntityUid uid, ActivatableUIComponent? aui = null)
{
2023-07-08 09:02:17 -07:00
if (!Resolve(uid, ref aui, false))
return;
2023-10-11 02:17:59 -07:00
if (aui.Key == null || !_uiSystem.TryGetUi(uid, aui.Key, out var ui))
2023-07-08 09:02:17 -07:00
return;
2023-10-11 02:17:59 -07:00
_uiSystem.CloseAll(ui);
}
2023-04-23 12:25:12 +10:00
private void OnHandDeselected(EntityUid uid, ActivatableUIComponent? aui, HandDeselectedEvent args)
{
2023-10-11 02:17:59 -07:00
if (!Resolve(uid, ref aui, false))
return;
2023-04-23 12:25:12 +10:00
if (!aui.CloseOnHandDeselect)
return;
2023-10-11 02:17:59 -07:00
2023-04-23 12:25:12 +10:00
CloseAll(uid, aui);
}
2023-04-23 12:25:12 +10:00
}