Activatable UI component (#5184)
* Transfer most Instrument UI logic to a new component, ActivatableUIComponent * Move more ActivatableUIComponent stuff to ECS * ActivatableUI component ignore on client * ActivatableUI: Get rid of component interfaces where possible * Add in adminOnly attribute for activatable UIs This is so that porting #4926 to this will be easier * Transition Solar Control Computer to ActivatableUI * Move communications console to ActivatableUI * Move cargo console to ActivatableUI * Move ID card console to ActivatableUI * ActivatableUI: Make things more amiable to entity tests adding components weirdly * ActivatableUI: Use handling or lack thereof of events properly * ActivatableUI: component dependency issue resolution stuffs * ActivatableUISystem: Fix #5258 * More fixes because master did stuffo * Check for HandDeselectedEvent again because otherwise active-hand check doesn't work * Move just a bit more code into the system, introduce a workaround for #5258 * Purge the player status detection stuff * Oh and some obsolete stuff too
This commit is contained in:
@@ -216,6 +216,8 @@ namespace Content.Client.Entry
|
|||||||
"Log",
|
"Log",
|
||||||
"Hoe",
|
"Hoe",
|
||||||
"Seed",
|
"Seed",
|
||||||
|
"ActivatableUI",
|
||||||
|
"ActivatableUIRequiresPower",
|
||||||
"BotanySharp",
|
"BotanySharp",
|
||||||
"PlantSampleTaker",
|
"PlantSampleTaker",
|
||||||
"Internals",
|
"Internals",
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ using Robust.Shared.ViewVariables;
|
|||||||
namespace Content.Server.Access.Components
|
namespace Content.Server.Access.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[ComponentReference(typeof(IActivate))]
|
public class IdCardConsoleComponent : SharedIdCardConsoleComponent, IInteractUsing, IBreakAct
|
||||||
public class IdCardConsoleComponent : SharedIdCardConsoleComponent, IActivate, IInteractUsing, IBreakAct
|
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
|
||||||
@@ -207,17 +206,6 @@ namespace Content.Server.Access.Components
|
|||||||
UserInterface?.SetState(newState);
|
UserInterface?.SetState(newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (!eventArgs.User.TryGetComponent(out ActorComponent? actor))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!Powered) return;
|
|
||||||
|
|
||||||
UserInterface?.Open(actor.PlayerSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
var item = eventArgs.Using;
|
var item = eventArgs.Using;
|
||||||
|
|||||||
@@ -1,142 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Content.Server.Power.Components;
|
|
||||||
using Content.Server.UserInterface;
|
|
||||||
using Content.Shared.ActionBlocker;
|
|
||||||
using Content.Shared.Interaction;
|
|
||||||
using Content.Shared.Popups;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// This component is used as a base class for classes like SolarControlConsoleComponent.
|
|
||||||
/// These components operate the server-side logic for the "primary UI" of a computer.
|
|
||||||
/// That means showing the UI when a user activates it, for example.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class BaseComputerUserInterfaceComponent : Component
|
|
||||||
{
|
|
||||||
protected readonly object UserInterfaceKey;
|
|
||||||
|
|
||||||
[ViewVariables] protected BoundUserInterface? UserInterface => Owner.GetUIOrNull(UserInterfaceKey);
|
|
||||||
[ViewVariables] public bool Powered => !Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || receiver.Powered;
|
|
||||||
|
|
||||||
public BaseComputerUserInterfaceComponent(object key)
|
|
||||||
{
|
|
||||||
UserInterfaceKey = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
if (UserInterface != null)
|
|
||||||
UserInterface.OnReceiveMessage += OnReceiveUIMessageCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Internal callback used to grab session and session attached entity before any more work is done.
|
|
||||||
/// This is so that sessionEntity is always available to checks up and down the line.
|
|
||||||
/// </summary>
|
|
||||||
private void OnReceiveUIMessageCallback(ServerBoundUserInterfaceMessage obj)
|
|
||||||
{
|
|
||||||
var session = obj.Session;
|
|
||||||
var sessionEntity = session.AttachedEntity;
|
|
||||||
if (sessionEntity == null)
|
|
||||||
return; // No session entity, so we're probably not able to touch this.
|
|
||||||
OnReceiveUnfilteredUserInterfaceMessage(obj, sessionEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Override this to handle messages from the UI before filtering them.
|
|
||||||
/// Calling base is necessary if you want this class to have any meaning.
|
|
||||||
/// </summary>
|
|
||||||
protected void OnReceiveUnfilteredUserInterfaceMessage(ServerBoundUserInterfaceMessage obj, IEntity sessionEntity)
|
|
||||||
{
|
|
||||||
// "Across all computers" "anti-cheats" ought to be put here or at some parent level (BaseDeviceUserInterfaceComponent?)
|
|
||||||
// Determine some facts about the session.
|
|
||||||
// Powered?
|
|
||||||
if (!Powered)
|
|
||||||
{
|
|
||||||
sessionEntity.PopupMessageCursor(Loc.GetString("base-computer-ui-component-not-powered"));
|
|
||||||
return; // Not powered, so this computer should probably do nothing.
|
|
||||||
}
|
|
||||||
// Can we interact?
|
|
||||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(sessionEntity.Uid))
|
|
||||||
{
|
|
||||||
sessionEntity.PopupMessageCursor(Loc.GetString("base-computer-ui-component-cannot-interact"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Good to go!
|
|
||||||
OnReceiveUserInterfaceMessage(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Override this to handle messages from the UI.
|
|
||||||
/// Calling base is unnecessary.
|
|
||||||
/// These messages will automatically be blocked if the user shouldn't be able to access this computer, or if the computer has lost power.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual void OnReceiveUserInterfaceMessage(ServerBoundUserInterfaceMessage obj)
|
|
||||||
{
|
|
||||||
// Nothing!
|
|
||||||
}
|
|
||||||
|
|
||||||
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
|
||||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
|
||||||
{
|
|
||||||
#pragma warning disable 618
|
|
||||||
base.HandleMessage(message, component);
|
|
||||||
#pragma warning restore 618
|
|
||||||
switch (message)
|
|
||||||
{
|
|
||||||
case PowerChangedMessage powerChanged:
|
|
||||||
PowerReceiverOnOnPowerStateChanged(powerChanged);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PowerReceiverOnOnPowerStateChanged(PowerChangedMessage e)
|
|
||||||
{
|
|
||||||
if (!e.Powered)
|
|
||||||
{
|
|
||||||
// We need to kick off users who are using it when it loses power.
|
|
||||||
UserInterface?.CloseAll();
|
|
||||||
// Now alert subclass.
|
|
||||||
ComputerLostPower();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Override this if you want the computer to do something when it loses power (i.e. reset state)
|
|
||||||
/// All UIs should have been closed by the time this is called.
|
|
||||||
/// Calling base is unnecessary.
|
|
||||||
/// </summary>
|
|
||||||
public virtual void ComputerLostPower()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This is called from ComputerUIActivatorSystem.
|
|
||||||
/// Override this to add additional activation conditions of some sort.
|
|
||||||
/// Calling base runs standard activation logic.
|
|
||||||
/// *This remains inside the component for overridability.*
|
|
||||||
/// </summary>
|
|
||||||
public virtual void ActivateThunk(ActivateInWorldEvent eventArgs)
|
|
||||||
{
|
|
||||||
if (!eventArgs.User.TryGetComponent(out ActorComponent? actor))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Powered)
|
|
||||||
{
|
|
||||||
Owner.PopupMessage(eventArgs.User, Loc.GetString("base-computer-ui-component-not-powered"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UserInterface?.Open(actor.PlayerSession);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,8 +19,7 @@ using Robust.Shared.ViewVariables;
|
|||||||
namespace Content.Server.Cargo.Components
|
namespace Content.Server.Cargo.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[ComponentReference(typeof(IActivate))]
|
public class CargoConsoleComponent : SharedCargoConsoleComponent
|
||||||
public class CargoConsoleComponent : SharedCargoConsoleComponent, IActivate
|
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
|
||||||
@@ -203,18 +202,6 @@ namespace Content.Server.Cargo.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (!eventArgs.User.TryGetComponent(out ActorComponent? actor))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!Powered)
|
|
||||||
return;
|
|
||||||
|
|
||||||
UserInterface?.Open(actor.PlayerSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateUIState()
|
private void UpdateUIState()
|
||||||
{
|
{
|
||||||
if (_bankAccount == null || !Owner.IsValid())
|
if (_bankAccount == null || !Owner.IsValid())
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Server.Cleanable;
|
using Content.Server.Cleanable;
|
||||||
using Content.Server.Coordinates.Helpers;
|
using Content.Server.Coordinates.Helpers;
|
||||||
using Content.Server.GameObjects.Components;
|
|
||||||
using Content.Shared.Chemistry;
|
using Content.Shared.Chemistry;
|
||||||
using Content.Shared.Chemistry.Reaction;
|
using Content.Shared.Chemistry.Reaction;
|
||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ using Timer = Robust.Shared.Timing.Timer;
|
|||||||
namespace Content.Server.Communications
|
namespace Content.Server.Communications
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[ComponentReference(typeof(IActivate))]
|
public class CommunicationsConsoleComponent : SharedCommunicationsConsoleComponent
|
||||||
public class CommunicationsConsoleComponent : SharedCommunicationsConsoleComponent, IActivate
|
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||||
@@ -121,23 +120,5 @@ namespace Content.Server.Communications
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenUserInterface(IPlayerSession session)
|
|
||||||
{
|
|
||||||
UserInterface?.Open(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (!eventArgs.User.TryGetComponent(out ActorComponent? actor))
|
|
||||||
return;
|
|
||||||
/*
|
|
||||||
if (!Powered)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
OpenUserInterface(actor.PlayerSession);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
using Content.Server.GameObjects.Components;
|
|
||||||
using Content.Shared.Interaction;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
|
|
||||||
namespace Content.Server.Computer
|
|
||||||
{
|
|
||||||
[UsedImplicitly]
|
|
||||||
internal sealed class ComputerUIActivatorSystem : EntitySystem
|
|
||||||
{
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
SubscribeLocalEvent<BaseComputerUserInterfaceComponent, ActivateInWorldEvent>(HandleActivate);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleActivate(EntityUid uid, BaseComputerUserInterfaceComponent component, ActivateInWorldEvent args)
|
|
||||||
{
|
|
||||||
component.ActivateThunk(args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.GameObjects.Components;
|
|
||||||
using Content.Server.WireHacking;
|
using Content.Server.WireHacking;
|
||||||
using Content.Shared.Construction;
|
using Content.Shared.Construction;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
|
|||||||
@@ -25,27 +25,11 @@ namespace Content.Server.Instruments
|
|||||||
{
|
{
|
||||||
|
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[ComponentReference(typeof(IActivate))]
|
|
||||||
public class InstrumentComponent
|
public class InstrumentComponent
|
||||||
: SharedInstrumentComponent,
|
: SharedInstrumentComponent
|
||||||
IDropped,
|
|
||||||
IHandSelected,
|
|
||||||
IHandDeselected,
|
|
||||||
IActivate,
|
|
||||||
IUse,
|
|
||||||
IThrown
|
|
||||||
{
|
{
|
||||||
private InstrumentSystem _instrumentSystem = default!;
|
private InstrumentSystem _instrumentSystem = default!;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The client channel currently playing the instrument, or null if there's none.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
private IPlayerSession? _instrumentPlayer;
|
|
||||||
|
|
||||||
[DataField("handheld")]
|
|
||||||
private bool _handheld;
|
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
private bool _playing = false;
|
private bool _playing = false;
|
||||||
|
|
||||||
@@ -115,11 +99,7 @@ namespace Content.Server.Instruments
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public IPlayerSession? InstrumentPlayer => Owner.GetComponentOrNull<ActivatableUIComponent>()?.CurrentSingleUser;
|
||||||
/// Whether the instrument is an item which can be held or not.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public bool Handheld => _handheld;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the instrument is currently playing or not.
|
/// Whether the instrument is currently playing or not.
|
||||||
@@ -135,41 +115,12 @@ namespace Content.Server.Instruments
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPlayerSession? InstrumentPlayer
|
|
||||||
{
|
|
||||||
get => _instrumentPlayer;
|
|
||||||
private set
|
|
||||||
{
|
|
||||||
Playing = false;
|
|
||||||
|
|
||||||
if (_instrumentPlayer != null)
|
|
||||||
_instrumentPlayer.PlayerStatusChanged -= OnPlayerStatusChanged;
|
|
||||||
|
|
||||||
_instrumentPlayer = value;
|
|
||||||
|
|
||||||
if (value != null)
|
|
||||||
_instrumentPlayer!.PlayerStatusChanged += OnPlayerStatusChanged;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(InstrumentUiKey.Key);
|
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(InstrumentUiKey.Key);
|
||||||
|
|
||||||
private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.Session != _instrumentPlayer || e.NewStatus != SessionStatus.Disconnected) return;
|
|
||||||
InstrumentPlayer = null;
|
|
||||||
Clean();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Initialize()
|
protected override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
if (UserInterface != null)
|
|
||||||
{
|
|
||||||
UserInterface.OnClosed += UserInterfaceOnClosed;
|
|
||||||
}
|
|
||||||
|
|
||||||
_instrumentSystem = EntitySystem.Get<InstrumentSystem>();
|
_instrumentSystem = EntitySystem.Get<InstrumentSystem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +141,7 @@ namespace Content.Server.Instruments
|
|||||||
switch (message)
|
switch (message)
|
||||||
{
|
{
|
||||||
case InstrumentMidiEventMessage midiEventMsg:
|
case InstrumentMidiEventMessage midiEventMsg:
|
||||||
if (!Playing || session != _instrumentPlayer || InstrumentPlayer == null) return;
|
if (!Playing || session != InstrumentPlayer || InstrumentPlayer == null) return;
|
||||||
|
|
||||||
var send = true;
|
var send = true;
|
||||||
|
|
||||||
@@ -237,12 +188,12 @@ namespace Content.Server.Instruments
|
|||||||
_lastSequencerTick = Math.Max(maxTick, minTick);
|
_lastSequencerTick = Math.Max(maxTick, minTick);
|
||||||
break;
|
break;
|
||||||
case InstrumentStartMidiMessage startMidi:
|
case InstrumentStartMidiMessage startMidi:
|
||||||
if (session != _instrumentPlayer)
|
if (session != InstrumentPlayer)
|
||||||
break;
|
break;
|
||||||
Playing = true;
|
Playing = true;
|
||||||
break;
|
break;
|
||||||
case InstrumentStopMidiMessage stopMidi:
|
case InstrumentStopMidiMessage stopMidi:
|
||||||
if (session != _instrumentPlayer)
|
if (session != InstrumentPlayer)
|
||||||
break;
|
break;
|
||||||
Playing = false;
|
Playing = false;
|
||||||
Clean();
|
Clean();
|
||||||
@@ -250,99 +201,20 @@ namespace Content.Server.Instruments
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Clean()
|
public void Clean()
|
||||||
{
|
{
|
||||||
|
if (Playing)
|
||||||
|
{
|
||||||
|
#pragma warning disable 618
|
||||||
|
SendNetworkMessage(new InstrumentStopMidiMessage());
|
||||||
|
#pragma warning restore 618
|
||||||
|
}
|
||||||
Playing = false;
|
Playing = false;
|
||||||
_lastSequencerTick = 0;
|
_lastSequencerTick = 0;
|
||||||
_batchesDropped = 0;
|
_batchesDropped = 0;
|
||||||
_laggedBatches = 0;
|
_laggedBatches = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IDropped.Dropped(DroppedEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
Clean();
|
|
||||||
#pragma warning disable 618
|
|
||||||
SendNetworkMessage(new InstrumentStopMidiMessage());
|
|
||||||
#pragma warning restore 618
|
|
||||||
InstrumentPlayer = null;
|
|
||||||
UserInterface?.CloseAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
void IThrown.Thrown(ThrownEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
Clean();
|
|
||||||
#pragma warning disable 618
|
|
||||||
SendNetworkMessage(new InstrumentStopMidiMessage());
|
|
||||||
#pragma warning restore 618
|
|
||||||
InstrumentPlayer = null;
|
|
||||||
UserInterface?.CloseAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
void IHandSelected.HandSelected(HandSelectedEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (eventArgs.User == null || !eventArgs.User.TryGetComponent(out ActorComponent? actor))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var session = actor.PlayerSession;
|
|
||||||
|
|
||||||
if (session.Status != SessionStatus.InGame) return;
|
|
||||||
|
|
||||||
InstrumentPlayer = session;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IHandDeselected.HandDeselected(HandDeselectedEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
Clean();
|
|
||||||
#pragma warning disable 618
|
|
||||||
SendNetworkMessage(new InstrumentStopMidiMessage());
|
|
||||||
#pragma warning restore 618
|
|
||||||
UserInterface?.CloseAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (Handheld)
|
|
||||||
return;
|
|
||||||
|
|
||||||
InteractInstrument(eventArgs.User);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IUse.UseEntity(UseEntityEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
InteractInstrument(eventArgs.User);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InteractInstrument(IEntity user)
|
|
||||||
{
|
|
||||||
if (!user.TryGetComponent(out ActorComponent? actor)) return;
|
|
||||||
|
|
||||||
if ((!Handheld && InstrumentPlayer != null)
|
|
||||||
|| (Handheld && actor.PlayerSession != InstrumentPlayer)
|
|
||||||
|| !EntitySystem.Get<ActionBlockerSystem>().CanInteract(user.Uid)) return;
|
|
||||||
|
|
||||||
InstrumentPlayer = actor.PlayerSession;
|
|
||||||
OpenUserInterface(InstrumentPlayer);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UserInterfaceOnClosed(IPlayerSession player)
|
|
||||||
{
|
|
||||||
if (Handheld || player != InstrumentPlayer) return;
|
|
||||||
|
|
||||||
Clean();
|
|
||||||
InstrumentPlayer = null;
|
|
||||||
#pragma warning disable 618
|
|
||||||
SendNetworkMessage(new InstrumentStopMidiMessage());
|
|
||||||
#pragma warning restore 618
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OpenUserInterface(IPlayerSession session)
|
|
||||||
{
|
|
||||||
UserInterface?.Toggle(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update(float delta)
|
public override void Update(float delta)
|
||||||
{
|
{
|
||||||
base.Update(delta);
|
base.Update(delta);
|
||||||
@@ -350,37 +222,22 @@ namespace Content.Server.Instruments
|
|||||||
var maxMidiLaggedBatches = _instrumentSystem.MaxMidiLaggedBatches;
|
var maxMidiLaggedBatches = _instrumentSystem.MaxMidiLaggedBatches;
|
||||||
var maxMidiBatchDropped = _instrumentSystem.MaxMidiBatchesDropped;
|
var maxMidiBatchDropped = _instrumentSystem.MaxMidiBatchesDropped;
|
||||||
|
|
||||||
if (_instrumentPlayer != null
|
|
||||||
&& (_instrumentPlayer.AttachedEntityUid == null
|
|
||||||
|| !EntitySystem.Get<ActionBlockerSystem>().CanInteract(_instrumentPlayer.AttachedEntityUid.Value)))
|
|
||||||
{
|
|
||||||
InstrumentPlayer = null;
|
|
||||||
Clean();
|
|
||||||
UserInterface?.CloseAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((_batchesDropped >= maxMidiBatchDropped
|
if ((_batchesDropped >= maxMidiBatchDropped
|
||||||
|| _laggedBatches >= maxMidiLaggedBatches)
|
|| _laggedBatches >= maxMidiLaggedBatches)
|
||||||
&& InstrumentPlayer != null && _respectMidiLimits)
|
&& InstrumentPlayer != null && _respectMidiLimits)
|
||||||
{
|
{
|
||||||
var mob = InstrumentPlayer.AttachedEntity;
|
var mob = InstrumentPlayer.AttachedEntity;
|
||||||
|
|
||||||
#pragma warning disable 618
|
// Just in case
|
||||||
SendNetworkMessage(new InstrumentStopMidiMessage());
|
Clean();
|
||||||
#pragma warning restore 618
|
|
||||||
Playing = false;
|
|
||||||
|
|
||||||
UserInterface?.CloseAll();
|
UserInterface?.CloseAll();
|
||||||
|
|
||||||
if (mob != null)
|
if (mob != null)
|
||||||
{
|
{
|
||||||
EntitySystem.Get<StunSystem>().TryParalyze(mob.Uid, TimeSpan.FromSeconds(1));
|
EntitySystem.Get<StunSystem>().TryParalyze(mob.Uid, TimeSpan.FromSeconds(1));
|
||||||
Clean();
|
|
||||||
|
|
||||||
Owner.PopupMessage(mob, "instrument-component-finger-cramps-max-message");
|
Owner.PopupMessage(mob, "instrument-component-finger-cramps-max-message");
|
||||||
}
|
}
|
||||||
|
|
||||||
InstrumentPlayer = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_timer += delta;
|
_timer += delta;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Content.Shared;
|
using Content.Shared;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
|
using Content.Server.UserInterface;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
@@ -20,6 +21,8 @@ namespace Content.Server.Instruments
|
|||||||
_cfg.OnValueChanged(CCVars.MaxMidiEventsPerBatch, OnMaxMidiEventsPerBatchChanged, true);
|
_cfg.OnValueChanged(CCVars.MaxMidiEventsPerBatch, OnMaxMidiEventsPerBatchChanged, true);
|
||||||
_cfg.OnValueChanged(CCVars.MaxMidiBatchesDropped, OnMaxMidiBatchesDroppedChanged, true);
|
_cfg.OnValueChanged(CCVars.MaxMidiBatchesDropped, OnMaxMidiBatchesDroppedChanged, true);
|
||||||
_cfg.OnValueChanged(CCVars.MaxMidiLaggedBatches, OnMaxMidiLaggedBatchesChanged, true);
|
_cfg.OnValueChanged(CCVars.MaxMidiLaggedBatches, OnMaxMidiLaggedBatchesChanged, true);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<InstrumentComponent, ActivatableUIPlayerChangedEvent>(InstrumentNeedsClean);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int MaxMidiEventsPerSecond { get; private set; }
|
public int MaxMidiEventsPerSecond { get; private set; }
|
||||||
@@ -47,6 +50,11 @@ namespace Content.Server.Instruments
|
|||||||
MaxMidiEventsPerSecond = obj;
|
MaxMidiEventsPerSecond = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void InstrumentNeedsClean(EntityUid uid, InstrumentComponent component, ActivatableUIPlayerChangedEvent ev)
|
||||||
|
{
|
||||||
|
component.Clean();
|
||||||
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
base.Update(frameTime);
|
base.Update(frameTime);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using Content.Server.Containers;
|
using Content.Server.Containers;
|
||||||
using Content.Server.GameObjects;
|
|
||||||
using Content.Server.Objectives.Interfaces;
|
using Content.Server.Objectives.Interfaces;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.Power.Components
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
public class ActivatableUIRequiresPowerComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "ActivatableUIRequiresPower";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Content.Shared;
|
||||||
|
using Content.Shared.CCVar;
|
||||||
|
using Content.Shared.ActionBlocker;
|
||||||
|
using Content.Shared.Hands;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Standing;
|
||||||
|
using Content.Shared.Stunnable;
|
||||||
|
using Content.Shared.Throwing;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Interaction.Helpers;
|
||||||
|
using Content.Server.Power.Components;
|
||||||
|
using Content.Server.UserInterface;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Server.Player;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
|
namespace Content.Server.Power.EntitySystems
|
||||||
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
|
internal sealed class ActivatableUIRequiresPowerSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly ActivatableUISystem _activatableUISystem = default!;
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ActivatableUIRequiresPowerComponent, ActivatableUIOpenAttemptEvent>(OnActivate);
|
||||||
|
SubscribeLocalEvent<ActivatableUIRequiresPowerComponent, PowerChangedEvent>(OnPowerChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnActivate(EntityUid uid, ActivatableUIRequiresPowerComponent component, ActivatableUIOpenAttemptEvent args)
|
||||||
|
{
|
||||||
|
if (args.Cancelled) return;
|
||||||
|
if (EntityManager.TryGetComponent<ApcPowerReceiverComponent>(uid, out var power) && !power.Powered)
|
||||||
|
{
|
||||||
|
args.User.PopupMessageCursor(Loc.GetString("base-computer-ui-component-not-powered"));
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPowerChanged(EntityUid uid, ActivatableUIRequiresPowerComponent component, PowerChangedEvent args)
|
||||||
|
{
|
||||||
|
if (!args.Powered)
|
||||||
|
_activatableUISystem.CloseAll(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using Content.Shared.Solar;
|
using Content.Shared.Solar;
|
||||||
using Content.Server.Solar.EntitySystems;
|
using Content.Server.Solar.EntitySystems;
|
||||||
using Content.Server.GameObjects.Components;
|
using Content.Server.UserInterface;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
@@ -11,45 +11,8 @@ using Robust.Shared.Maths;
|
|||||||
namespace Content.Server.Solar.Components
|
namespace Content.Server.Solar.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[ComponentReference(typeof(BaseComputerUserInterfaceComponent))]
|
public class SolarControlConsoleComponent : Component
|
||||||
public class SolarControlConsoleComponent : BaseComputerUserInterfaceComponent
|
|
||||||
{
|
{
|
||||||
public override string Name => "SolarControlConsole";
|
public override string Name => "SolarControlConsole";
|
||||||
|
|
||||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
|
||||||
|
|
||||||
private PowerSolarSystem _powerSolarSystem = default!;
|
|
||||||
|
|
||||||
public SolarControlConsoleComponent() : base(SolarControlConsoleUiKey.Key) { }
|
|
||||||
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
_powerSolarSystem = _entitySystemManager.GetEntitySystem<PowerSolarSystem>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateUIState()
|
|
||||||
{
|
|
||||||
UserInterface?.SetState(new SolarControlConsoleBoundInterfaceState(_powerSolarSystem.TargetPanelRotation, _powerSolarSystem.TargetPanelVelocity, _powerSolarSystem.TotalPanelPower, _powerSolarSystem.TowardsSun));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnReceiveUserInterfaceMessage(ServerBoundUserInterfaceMessage obj)
|
|
||||||
{
|
|
||||||
switch (obj.Message)
|
|
||||||
{
|
|
||||||
case SolarControlConsoleAdjustMessage msg:
|
|
||||||
if (double.IsFinite(msg.Rotation))
|
|
||||||
{
|
|
||||||
_powerSolarSystem.TargetPanelRotation = msg.Rotation.Reduced();
|
|
||||||
}
|
|
||||||
if (double.IsFinite(msg.AngularVelocity))
|
|
||||||
{
|
|
||||||
var degrees = msg.AngularVelocity.Degrees;
|
|
||||||
degrees = Math.Clamp(degrees, -PowerSolarSystem.MaxPanelVelocityDegrees, PowerSolarSystem.MaxPanelVelocityDegrees);
|
|
||||||
_powerSolarSystem.TargetPanelVelocity = Angle.FromDegrees(degrees);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
|
using System;
|
||||||
using Content.Server.Solar.Components;
|
using Content.Server.Solar.Components;
|
||||||
|
using Content.Server.UserInterface;
|
||||||
|
using Content.Shared.Solar;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
|
||||||
namespace Content.Server.Solar.EntitySystems
|
namespace Content.Server.Solar.EntitySystems
|
||||||
{
|
{
|
||||||
@@ -10,22 +16,53 @@ namespace Content.Server.Solar.EntitySystems
|
|||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
internal sealed class PowerSolarControlConsoleSystem : EntitySystem
|
internal sealed class PowerSolarControlConsoleSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private PowerSolarSystem _powerSolarSystem = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Timer used to avoid updating the UI state every frame (which would be overkill)
|
/// Timer used to avoid updating the UI state every frame (which would be overkill)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private float _updateTimer;
|
private float _updateTimer;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<SolarControlConsoleComponent, ServerBoundUserInterfaceMessage>(OnUIMessage);
|
||||||
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
_updateTimer += frameTime;
|
_updateTimer += frameTime;
|
||||||
if (_updateTimer >= 1)
|
if (_updateTimer >= 1)
|
||||||
{
|
{
|
||||||
_updateTimer -= 1;
|
_updateTimer -= 1;
|
||||||
|
var state = new SolarControlConsoleBoundInterfaceState(_powerSolarSystem.TargetPanelRotation, _powerSolarSystem.TargetPanelVelocity, _powerSolarSystem.TotalPanelPower, _powerSolarSystem.TowardsSun);
|
||||||
foreach (var component in EntityManager.EntityQuery<SolarControlConsoleComponent>())
|
foreach (var component in EntityManager.EntityQuery<SolarControlConsoleComponent>())
|
||||||
{
|
{
|
||||||
component.UpdateUIState();
|
component.Owner.GetUIOrNull(SolarControlConsoleUiKey.Key)?.SetState(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnUIMessage(EntityUid uid, SolarControlConsoleComponent component, ServerBoundUserInterfaceMessage obj)
|
||||||
|
{
|
||||||
|
if (component.Deleted) return;
|
||||||
|
switch (obj.Message)
|
||||||
|
{
|
||||||
|
case SolarControlConsoleAdjustMessage msg:
|
||||||
|
if (double.IsFinite(msg.Rotation))
|
||||||
|
{
|
||||||
|
_powerSolarSystem.TargetPanelRotation = msg.Rotation.Reduced();
|
||||||
|
}
|
||||||
|
if (double.IsFinite(msg.AngularVelocity))
|
||||||
|
{
|
||||||
|
var degrees = msg.AngularVelocity.Degrees;
|
||||||
|
degrees = Math.Clamp(degrees, -PowerSolarSystem.MaxPanelVelocityDegrees, PowerSolarSystem.MaxPanelVelocityDegrees);
|
||||||
|
_powerSolarSystem.TargetPanelVelocity = Angle.FromDegrees(degrees);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
60
Content.Server/UserInterface/ActivatableUIComponent.cs
Normal file
60
Content.Server/UserInterface/ActivatableUIComponent.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Shared.Instruments;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Server.Player;
|
||||||
|
using Robust.Shared.Reflection;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Enums;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
|
namespace Content.Server.UserInterface
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
public class ActivatableUIComponent : Component,
|
||||||
|
ISerializationHooks
|
||||||
|
{
|
||||||
|
public override string Name => "ActivatableUI";
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public Enum? Key { get; set; }
|
||||||
|
|
||||||
|
[ViewVariables] public BoundUserInterface? UserInterface => (Key != null) ? Owner.GetUIOrNull(Key) : null;
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("inHandsOnly")]
|
||||||
|
public bool InHandsOnly { get; set; } = false;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("singleUser")]
|
||||||
|
public bool SingleUser { get; set; } = false;
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("adminOnly")]
|
||||||
|
public bool AdminOnly { get; set; } = false;
|
||||||
|
|
||||||
|
[DataField("key", readOnly: true, required: true)]
|
||||||
|
private string _keyRaw = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The client channel currently using the object, or null if there's none/not single user.
|
||||||
|
/// NOTE: DO NOT DIRECTLY SET, USE ActivatableUISystem.SetCurrentSingleUser
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public IPlayerSession? CurrentSingleUser;
|
||||||
|
|
||||||
|
void ISerializationHooks.AfterDeserialization()
|
||||||
|
{
|
||||||
|
var reflectionManager = IoCManager.Resolve<IReflectionManager>();
|
||||||
|
if (reflectionManager.TryParseEnumReference(_keyRaw, out var key))
|
||||||
|
Key = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
154
Content.Server/UserInterface/ActivatableUISystem.cs
Normal file
154
Content.Server/UserInterface/ActivatableUISystem.cs
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Content.Shared;
|
||||||
|
using Content.Shared.CCVar;
|
||||||
|
using Content.Shared.ActionBlocker;
|
||||||
|
using Content.Shared.Hands;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Standing;
|
||||||
|
using Content.Shared.Stunnable;
|
||||||
|
using Content.Shared.Throwing;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Interaction.Helpers;
|
||||||
|
using Content.Server.Administration.Managers;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Server.Player;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
|
namespace Content.Server.UserInterface
|
||||||
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
|
internal sealed class ActivatableUISystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||||
|
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ActivatableUIComponent, ActivateInWorldEvent>(OnActivate);
|
||||||
|
SubscribeLocalEvent<ActivatableUIComponent, UseInHandEvent>(OnUseInHand);
|
||||||
|
SubscribeLocalEvent<ActivatableUIComponent, HandDeselectedEvent>((uid, aui, _) => CloseAll(uid, aui));
|
||||||
|
SubscribeLocalEvent<ActivatableUIComponent, UnequippedHandEvent>((uid, aui, _) => CloseAll(uid, aui));
|
||||||
|
// *THIS IS A BLATANT WORKAROUND!* RATIONALE: Microwaves need it
|
||||||
|
SubscribeLocalEvent<ActivatableUIComponent, EntParentChangedMessage>(OnParentChanged);
|
||||||
|
SubscribeLocalEvent<ActivatableUIComponent, BoundUIClosedEvent>(OnUIClose);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnActivate(EntityUid uid, ActivatableUIComponent component, ActivateInWorldEvent args)
|
||||||
|
{
|
||||||
|
if (args.Handled) return;
|
||||||
|
if (component.InHandsOnly) return;
|
||||||
|
args.Handled = InteractUI(args.User, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUseInHand(EntityUid uid, ActivatableUIComponent component, UseInHandEvent args)
|
||||||
|
{
|
||||||
|
if (args.Handled) return;
|
||||||
|
args.Handled = InteractUI(args.User, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnParentChanged(EntityUid uid, ActivatableUIComponent aui, ref EntParentChangedMessage args)
|
||||||
|
{
|
||||||
|
CloseAll(uid, aui);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUIClose(EntityUid uid, ActivatableUIComponent component, BoundUIClosedEvent args)
|
||||||
|
{
|
||||||
|
if (args.Session != component.CurrentSingleUser) return;
|
||||||
|
if (args.UiKey != component.Key) return;
|
||||||
|
SetCurrentSingleUser(uid, null, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool InteractUI(IEntity user, ActivatableUIComponent aui)
|
||||||
|
{
|
||||||
|
if (!user.TryGetComponent(out ActorComponent? actor)) return false;
|
||||||
|
|
||||||
|
if (aui.AdminOnly && !_adminManager.IsAdmin(actor.PlayerSession)) return false;
|
||||||
|
|
||||||
|
if (!_actionBlockerSystem.CanInteract(user.Uid))
|
||||||
|
{
|
||||||
|
user.PopupMessageCursor(Loc.GetString("base-computer-ui-component-cannot-interact"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ui = aui.UserInterface;
|
||||||
|
if (ui == null) return false;
|
||||||
|
|
||||||
|
if (aui.SingleUser && (aui.CurrentSingleUser != null) && (actor.PlayerSession != aui.CurrentSingleUser))
|
||||||
|
{
|
||||||
|
// 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.
|
||||||
|
if (ui.SubscribedSessions.Count != 0) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
RaiseLocalEvent(aui.OwnerUid, oae, false);
|
||||||
|
if (oae.Cancelled) return false;
|
||||||
|
|
||||||
|
SetCurrentSingleUser(aui.OwnerUid, actor.PlayerSession, aui);
|
||||||
|
ui.Toggle(actor.PlayerSession);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCurrentSingleUser(EntityUid uid, IPlayerSession? v, ActivatableUIComponent? aui = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref aui))
|
||||||
|
return;
|
||||||
|
if (!aui.SingleUser)
|
||||||
|
return;
|
||||||
|
|
||||||
|
aui.CurrentSingleUser = v;
|
||||||
|
|
||||||
|
RaiseLocalEvent(uid, new ActivatableUIPlayerChangedEvent(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
foreach (var component in EntityManager.EntityQuery<ActivatableUIComponent>(true))
|
||||||
|
{
|
||||||
|
var ui = component.UserInterface;
|
||||||
|
if (ui == null) continue;
|
||||||
|
// Done to skip an allocation on anything that's not in use.
|
||||||
|
if (ui.SubscribedSessions.Count == 0) continue;
|
||||||
|
// Must ToList in order to close things safely.
|
||||||
|
foreach (var session in ui.SubscribedSessions.ToArray())
|
||||||
|
{
|
||||||
|
if (session.AttachedEntityUid == null || !_actionBlockerSystem.CanInteract(session.AttachedEntityUid.Value))
|
||||||
|
{
|
||||||
|
ui.Close(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CloseAll(EntityUid uid, ActivatableUIComponent? aui = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref aui, false)) return;
|
||||||
|
aui.UserInterface?.CloseAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ActivatableUIOpenAttemptEvent : CancellableEntityEventArgs
|
||||||
|
{
|
||||||
|
public IEntity User { get; }
|
||||||
|
public ActivatableUIOpenAttemptEvent(IEntity who)
|
||||||
|
{
|
||||||
|
User = who;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ActivatableUIPlayerChangedEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,10 @@
|
|||||||
description: That's an instrument.
|
description: That's an instrument.
|
||||||
components:
|
components:
|
||||||
- type: Instrument
|
- type: Instrument
|
||||||
handheld: true
|
- type: ActivatableUI
|
||||||
|
inHandsOnly: true
|
||||||
|
singleUser: true
|
||||||
|
key: enum.InstrumentUiKey.Key
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
- key: enum.InstrumentUiKey.Key
|
- key: enum.InstrumentUiKey.Key
|
||||||
|
|||||||
@@ -5,7 +5,10 @@
|
|||||||
abstract: true
|
abstract: true
|
||||||
components:
|
components:
|
||||||
- type: Instrument
|
- type: Instrument
|
||||||
handheld: false
|
- type: ActivatableUI
|
||||||
|
inHandsOnly: false
|
||||||
|
singleUser: true
|
||||||
|
key: enum.InstrumentUiKey.Key
|
||||||
- type: InteractionOutline
|
- type: InteractionOutline
|
||||||
- type: Rotatable
|
- type: Rotatable
|
||||||
rotateWhileAnchored: true
|
rotateWhileAnchored: true
|
||||||
|
|||||||
@@ -126,6 +126,9 @@
|
|||||||
- type: AccessReader
|
- type: AccessReader
|
||||||
access: [["HeadOfPersonnel"]]
|
access: [["HeadOfPersonnel"]]
|
||||||
- type: IdCardConsole
|
- type: IdCardConsole
|
||||||
|
- type: ActivatableUI
|
||||||
|
key: enum.IdCardConsoleUiKey.Key
|
||||||
|
- type: ActivatableUIRequiresPower
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
- key: enum.IdCardConsoleUiKey.Key
|
- key: enum.IdCardConsoleUiKey.Key
|
||||||
@@ -177,6 +180,9 @@
|
|||||||
key: generic_key
|
key: generic_key
|
||||||
screen: comm
|
screen: comm
|
||||||
- type: CommunicationsConsole
|
- type: CommunicationsConsole
|
||||||
|
- type: ActivatableUI
|
||||||
|
key: enum.CommunicationsConsoleUiKey.Key
|
||||||
|
- type: ActivatableUIRequiresPower
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
- key: enum.CommunicationsConsoleUiKey.Key
|
- key: enum.CommunicationsConsoleUiKey.Key
|
||||||
@@ -200,6 +206,9 @@
|
|||||||
key: generic_key
|
key: generic_key
|
||||||
screen: solar_screen
|
screen: solar_screen
|
||||||
- type: SolarControlConsole
|
- type: SolarControlConsole
|
||||||
|
- type: ActivatableUI
|
||||||
|
key: enum.SolarControlConsoleUiKey.Key
|
||||||
|
- type: ActivatableUIRequiresPower
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
- key: enum.SolarControlConsoleUiKey.Key
|
- key: enum.SolarControlConsoleUiKey.Key
|
||||||
@@ -278,6 +287,9 @@
|
|||||||
# - AtmosphericsWaterVapor
|
# - AtmosphericsWaterVapor
|
||||||
# - AtmosphericsPlasma
|
# - AtmosphericsPlasma
|
||||||
# - AtmosphericsTritium
|
# - AtmosphericsTritium
|
||||||
|
- type: ActivatableUI
|
||||||
|
key: enum.CargoConsoleUiKey.Key
|
||||||
|
- type: ActivatableUIRequiresPower
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
- key: enum.CargoConsoleUiKey.Key
|
- key: enum.CargoConsoleUiKey.Key
|
||||||
|
|||||||
Reference in New Issue
Block a user