GuideEntityEmbed.xaml.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. using System.Diagnostics.CodeAnalysis;
  2. using System.Globalization;
  3. using System.Linq;
  4. using System.Numerics;
  5. using Content.Client.ContextMenu.UI;
  6. using Content.Client.Examine;
  7. using Content.Client.Guidebook.Richtext;
  8. using Content.Client.Verbs.UI;
  9. using Content.Shared.Input;
  10. using Content.Shared.Tag;
  11. using Robust.Client.AutoGenerated;
  12. using Robust.Client.GameObjects;
  13. using Robust.Client.UserInterface;
  14. using Robust.Client.UserInterface.Controls;
  15. using Robust.Client.UserInterface.XAML;
  16. using Robust.Shared.Input;
  17. using Robust.Shared.Map;
  18. using Robust.Shared.Utility;
  19. namespace Content.Client.Guidebook.Controls;
  20. /// <summary>
  21. /// Control for embedding an entity into a guidebook/document. This is effectively a sprite-view that supports
  22. /// examination, interactions, and captions.
  23. /// </summary>
  24. [GenerateTypedNameReferences]
  25. public sealed partial class GuideEntityEmbed : BoxContainer, IDocumentTag
  26. {
  27. [Dependency] private readonly IEntityManager _entityManager = default!;
  28. [Dependency] private readonly IEntitySystemManager _systemManager = default!;
  29. [Dependency] private readonly IUserInterfaceManager _ui = default!;
  30. private readonly TagSystem _tagSystem;
  31. private readonly ExamineSystem _examineSystem;
  32. private readonly GuidebookSystem _guidebookSystem;
  33. public bool Interactive;
  34. public Entity<SpriteComponent>? Sprite => View.Entity == null || View.Sprite == null
  35. ? null
  36. : (View.Entity.Value, View.Sprite);
  37. public Vector2 Scale
  38. {
  39. get => View.Scale;
  40. set => View.Scale = value;
  41. }
  42. public GuideEntityEmbed()
  43. {
  44. RobustXamlLoader.Load(this);
  45. IoCManager.InjectDependencies(this);
  46. _tagSystem = _systemManager.GetEntitySystem<TagSystem>();
  47. _examineSystem = _systemManager.GetEntitySystem<ExamineSystem>();
  48. _guidebookSystem = _systemManager.GetEntitySystem<GuidebookSystem>();
  49. MouseFilter = MouseFilterMode.Stop;
  50. }
  51. public GuideEntityEmbed(string proto, bool caption, bool interactive) : this()
  52. {
  53. Interactive = interactive;
  54. var ent = _entityManager.SpawnEntity(proto, MapCoordinates.Nullspace);
  55. View.SetEntity(ent);
  56. if (caption)
  57. Caption.Text = _entityManager.GetComponent<MetaDataComponent>(ent).EntityName;
  58. }
  59. protected override void KeyBindDown(GUIBoundKeyEventArgs args)
  60. {
  61. base.KeyBindDown(args);
  62. // get an entity associated with this element
  63. var entity = Sprite?.Owner;
  64. // Deleted() automatically checks for null & existence.
  65. if (_entityManager.Deleted(entity))
  66. return;
  67. // do examination?
  68. if (args.Function == ContentKeyFunctions.ExamineEntity)
  69. {
  70. _examineSystem.DoExamine(entity.Value,
  71. userOverride: _guidebookSystem.GetGuidebookUser());
  72. args.Handle();
  73. return;
  74. }
  75. if (!Interactive)
  76. return;
  77. // open verb menu?
  78. if (args.Function == EngineKeyFunctions.UseSecondary)
  79. {
  80. _ui.GetUIController<VerbMenuUIController>().OpenVerbMenu(entity.Value);
  81. args.Handle();
  82. return;
  83. }
  84. // from here out we're faking interactions! sue me. --moony
  85. if (args.Function == ContentKeyFunctions.ActivateItemInWorld)
  86. {
  87. _guidebookSystem.FakeClientActivateInWorld(entity.Value);
  88. _ui.GetUIController<ContextMenuUIController>().Close();
  89. args.Handle();
  90. return;
  91. }
  92. if (args.Function == ContentKeyFunctions.AltActivateItemInWorld)
  93. {
  94. _guidebookSystem.FakeClientAltActivateInWorld(entity.Value);
  95. _ui.GetUIController<ContextMenuUIController>().Close();
  96. args.Handle();
  97. return;
  98. }
  99. if (args.Function == ContentKeyFunctions.AltActivateItemInWorld)
  100. {
  101. _guidebookSystem.FakeClientUse(entity.Value);
  102. _ui.GetUIController<ContextMenuUIController>().Close();
  103. args.Handle();
  104. }
  105. }
  106. protected override void Dispose(bool disposing)
  107. {
  108. base.Dispose(disposing);
  109. if (Sprite is not null)
  110. _entityManager.DeleteEntity(Sprite);
  111. }
  112. public bool TryParseTag(Dictionary<string, string> args, [NotNullWhen(true)] out Control? control)
  113. {
  114. if (!args.TryGetValue("Entity", out var proto))
  115. {
  116. Logger.Error("Entity embed tag is missing entity prototype argument");
  117. control = null;
  118. return false;
  119. }
  120. var ent = _entityManager.SpawnEntity(proto, MapCoordinates.Nullspace);
  121. _tagSystem.AddTag(ent, GuidebookSystem.GuideEmbedTag);
  122. View.SetEntity(ent);
  123. if (!args.TryGetValue("Caption", out var caption))
  124. caption = _entityManager.GetComponent<MetaDataComponent>(ent).EntityName;
  125. if (!string.IsNullOrEmpty(caption))
  126. Caption.Text = caption;
  127. // else:
  128. // caption text already defaults to null
  129. if (args.TryGetValue("Scale", out var scaleStr))
  130. {
  131. var scale = float.Parse(scaleStr, CultureInfo.InvariantCulture);
  132. Scale = new Vector2(scale, scale);
  133. }
  134. else
  135. {
  136. Scale = new Vector2(2, 2);
  137. }
  138. if (args.TryGetValue("Interactive", out var interactive))
  139. Interactive = bool.Parse(interactive);
  140. if (args.TryGetValue("Rotation", out var rotation))
  141. {
  142. View.OverrideDirection = Angle.FromDegrees(double.Parse(rotation)).GetDir();
  143. }
  144. if (args.TryGetValue("Margin", out var margin))
  145. {
  146. Margin = ParseThickness(margin);
  147. }
  148. else
  149. {
  150. Margin = new Thickness(4, 8);
  151. }
  152. // By default, we will map-initialize guidebook entities.
  153. if (!args.TryGetValue("Init", out var mapInit) || !bool.Parse(mapInit))
  154. _entityManager.RunMapInit(ent, _entityManager.GetComponent<MetaDataComponent>(ent));
  155. control = this;
  156. return true;
  157. }
  158. private static Thickness ParseThickness(string value)
  159. {
  160. if (string.IsNullOrWhiteSpace(value))
  161. return default;
  162. var split = value.Split(" ", StringSplitOptions.RemoveEmptyEntries).Select(x => Parse.Float(x)).ToArray();
  163. if (split.Length == 1)
  164. return new Thickness(split[0]);
  165. if (split.Length == 2)
  166. return new Thickness(split[0], split[1]);
  167. if (split.Length == 4)
  168. return new Thickness(split[0], split[1], split[2], split[3]);
  169. throw new Exception("Invalid Thickness format!");
  170. }
  171. }