diff --git a/Content.Client/Examine/ExamineSystem.cs b/Content.Client/Examine/ExamineSystem.cs index 0109f90cc5..9a563a8638 100644 --- a/Content.Client/Examine/ExamineSystem.cs +++ b/Content.Client/Examine/ExamineSystem.cs @@ -327,9 +327,9 @@ namespace Content.Client.Examine } } - public void DoExamine(EntityUid entity, bool centeredOnCursor=true) + public void DoExamine(EntityUid entity, bool centeredOnCursor = true, EntityUid? userOverride = null) { - var playerEnt = _playerManager.LocalPlayer?.ControlledEntity; + var playerEnt = userOverride ?? _playerManager.LocalPlayer?.ControlledEntity; if (playerEnt == null) return; diff --git a/Content.Client/Guidebook/Controls/GuideEntityEmbed.xaml.cs b/Content.Client/Guidebook/Controls/GuideEntityEmbed.xaml.cs index 34b4f3bdaa..fc885b3655 100644 --- a/Content.Client/Guidebook/Controls/GuideEntityEmbed.xaml.cs +++ b/Content.Client/Guidebook/Controls/GuideEntityEmbed.xaml.cs @@ -79,7 +79,8 @@ public sealed partial class GuideEntityEmbed : BoxContainer, IDocumentTag // do examination? if (args.Function == ContentKeyFunctions.ExamineEntity) { - _examineSystem.DoExamine(entity.Value); + _examineSystem.DoExamine(entity.Value, + userOverride: _guidebookSystem.GetGuidebookUser()); args.Handle(); return; } diff --git a/Content.Client/Guidebook/GuidebookSystem.cs b/Content.Client/Guidebook/GuidebookSystem.cs index acaa51524b..a3c55a0f0e 100644 --- a/Content.Client/Guidebook/GuidebookSystem.cs +++ b/Content.Client/Guidebook/GuidebookSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.Tag; using Content.Shared.Verbs; using Robust.Client.GameObjects; using Robust.Client.Player; +using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Utility; @@ -29,6 +30,8 @@ public sealed class GuidebookSystem : EntitySystem public event Action, List?, string?, bool, string?>? OnGuidebookOpen; public const string GuideEmbedTag = "GuideEmbeded"; + private EntityUid _defaultUser; + /// public override void Initialize() { @@ -41,6 +44,23 @@ public sealed class GuidebookSystem : EntitySystem OnGuidebookControlsTestGetAlternateVerbs); } + /// + /// Gets a user entity to use for verbs and examinations. If the player has no attached entity, this will use a + /// dummy client-side entity so that users can still use the guidebook when not attached to anything (e.g., in the + /// lobby) + /// + public EntityUid GetGuidebookUser() + { + var user = _playerManager.LocalPlayer!.ControlledEntity; + if (user != null) + return user.Value; + + if (!Exists(_defaultUser)) + _defaultUser = Spawn(null, MapCoordinates.Nullspace); + + return _defaultUser; + } + private void OnGetVerbs(EntityUid uid, GuideHelpComponent component, GetVerbsEvent args) { if (component.Guides.Count == 0 || _tags.HasTag(uid, GuideEmbedTag)) @@ -114,34 +134,26 @@ public sealed class GuidebookSystem : EntitySystem _audioSystem.PlayGlobal(speech.SpeechSounds, Filter.Local(), false, speech.AudioParams); } - public void FakeClientActivateInWorld(EntityUid activated) { - var user = _playerManager.LocalPlayer!.ControlledEntity; - if (user is null) - return; - var activateMsg = new ActivateInWorldEvent(user.Value, activated); - RaiseLocalEvent(activated, activateMsg, true); + var activateMsg = new ActivateInWorldEvent(GetGuidebookUser(), activated); + RaiseLocalEvent(activated, activateMsg); } public void FakeClientAltActivateInWorld(EntityUid activated) { - var user = _playerManager.LocalPlayer!.ControlledEntity; - if (user is null) - return; // Get list of alt-interact verbs - var verbs = _verbSystem.GetLocalVerbs(activated, user.Value, typeof(AlternativeVerb)); + var verbs = _verbSystem.GetLocalVerbs(activated, GetGuidebookUser(), typeof(AlternativeVerb), force: true); if (!verbs.Any()) return; - _verbSystem.ExecuteVerb(verbs.First(), user.Value, activated); + _verbSystem.ExecuteVerb(verbs.First(), GetGuidebookUser(), activated); } public void FakeClientUse(EntityUid activated) { - var user = _playerManager.LocalPlayer!.ControlledEntity ?? EntityUid.Invalid; - var activateMsg = new InteractHandEvent(user, activated); - RaiseLocalEvent(activated, activateMsg, true); + var activateMsg = new InteractHandEvent(GetGuidebookUser(), activated); + RaiseLocalEvent(activated, activateMsg); } } diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index c55c3df841..1fcd7fd184 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -214,7 +214,7 @@ namespace Content.Client.Verbs return; } - if (verb.ClientExclusive) + if (verb.ClientExclusive || target.IsClientSide()) // is this a client exclusive (gui) verb? ExecuteVerb(verb, user.Value, target); else diff --git a/Content.Shared/Verbs/Verb.cs b/Content.Shared/Verbs/Verb.cs index 047dfa5db9..cd5edc22d9 100644 --- a/Content.Shared/Verbs/Verb.cs +++ b/Content.Shared/Verbs/Verb.cs @@ -54,7 +54,8 @@ namespace Content.Shared.Verbs public EntityUid EventTarget = EntityUid.Invalid; /// - /// If a verb is only defined client-side, this should be set to true. + /// Whether a verb is only defined client-side. Note that this has nothing to do with whether the target of + /// the verb is client-side /// /// /// If true, the client will not also ask the server to run this verb when executed locally. This just