TippyUIController.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. using System.Numerics;
  2. using Content.Client.Message;
  3. using Content.Client.Paper.UI;
  4. using Content.Shared.CCVar;
  5. using Content.Shared.Movement.Components;
  6. using Content.Shared.Tips;
  7. using Robust.Client.GameObjects;
  8. using Robust.Client.ResourceManagement;
  9. using Robust.Client.UserInterface;
  10. using Robust.Client.UserInterface.Controllers;
  11. using Robust.Client.UserInterface.Controls;
  12. using Robust.Client.Audio;
  13. using Robust.Shared.Configuration;
  14. using Robust.Shared.Map;
  15. using Robust.Shared.Timing;
  16. using static Content.Client.Tips.TippyUI;
  17. namespace Content.Client.Tips;
  18. public sealed class TippyUIController : UIController
  19. {
  20. [Dependency] private readonly IConfigurationManager _cfg = default!;
  21. [Dependency] private readonly IResourceCache _resCache = default!;
  22. [UISystemDependency] private readonly AudioSystem _audio = default!;
  23. public const float Padding = 50;
  24. public static Angle WaddleRotation = Angle.FromDegrees(10);
  25. private EntityUid _entity;
  26. private float _secondsUntilNextState;
  27. private int _previousStep = 0;
  28. private TippyEvent? _currentMessage;
  29. private readonly Queue<TippyEvent> _queuedMessages = new();
  30. public override void Initialize()
  31. {
  32. base.Initialize();
  33. UIManager.OnScreenChanged += OnScreenChanged;
  34. SubscribeNetworkEvent<TippyEvent>(OnTippyEvent);
  35. }
  36. private void OnTippyEvent(TippyEvent msg, EntitySessionEventArgs args)
  37. {
  38. _queuedMessages.Enqueue(msg);
  39. }
  40. public override void FrameUpdate(FrameEventArgs args)
  41. {
  42. base.FrameUpdate(args);
  43. var screen = UIManager.ActiveScreen;
  44. if (screen == null)
  45. {
  46. _queuedMessages.Clear();
  47. return;
  48. }
  49. var tippy = screen.GetOrAddWidget<TippyUI>();
  50. _secondsUntilNextState -= args.DeltaSeconds;
  51. if (_secondsUntilNextState <= 0)
  52. NextState(tippy);
  53. else
  54. {
  55. var pos = UpdatePosition(tippy, screen.Size, args); ;
  56. LayoutContainer.SetPosition(tippy, pos);
  57. }
  58. }
  59. private Vector2 UpdatePosition(TippyUI tippy, Vector2 screenSize, FrameEventArgs args)
  60. {
  61. if (_currentMessage == null)
  62. return default;
  63. var slideTime = _currentMessage.SlideTime;
  64. var offset = tippy.State switch
  65. {
  66. TippyState.Hidden => 0,
  67. TippyState.Revealing => Math.Clamp(1 - _secondsUntilNextState / slideTime, 0, 1),
  68. TippyState.Hiding => Math.Clamp(_secondsUntilNextState / slideTime, 0, 1),
  69. _ => 1,
  70. };
  71. var waddle = _currentMessage.WaddleInterval;
  72. if (_currentMessage == null
  73. || waddle <= 0
  74. || tippy.State == TippyState.Hidden
  75. || tippy.State == TippyState.Speaking
  76. || !EntityManager.TryGetComponent(_entity, out SpriteComponent? sprite))
  77. {
  78. return new Vector2(screenSize.X - offset * (tippy.DesiredSize.X + Padding), (screenSize.Y - tippy.DesiredSize.Y) / 2);
  79. }
  80. var numSteps = (int) Math.Ceiling(slideTime / waddle);
  81. var curStep = (int) Math.Floor(numSteps * offset);
  82. var stepSize = (tippy.DesiredSize.X + Padding) / numSteps;
  83. if (curStep != _previousStep)
  84. {
  85. _previousStep = curStep;
  86. sprite.Rotation = sprite.Rotation > 0
  87. ? -WaddleRotation
  88. : WaddleRotation;
  89. if (EntityManager.TryGetComponent(_entity, out FootstepModifierComponent? step) && step.FootstepSoundCollection != null)
  90. {
  91. var audioParams = step.FootstepSoundCollection.Params
  92. .AddVolume(-7f)
  93. .WithVariation(0.1f);
  94. _audio.PlayGlobal(step.FootstepSoundCollection, EntityUid.Invalid, audioParams);
  95. }
  96. }
  97. return new Vector2(screenSize.X - stepSize * curStep, (screenSize.Y - tippy.DesiredSize.Y) / 2);
  98. }
  99. private void NextState(TippyUI tippy)
  100. {
  101. SpriteComponent? sprite;
  102. switch (tippy.State)
  103. {
  104. case TippyState.Hidden:
  105. if (!_queuedMessages.TryDequeue(out var next))
  106. return;
  107. if (next.Proto != null)
  108. {
  109. _entity = EntityManager.SpawnEntity(next.Proto, MapCoordinates.Nullspace);
  110. tippy.ModifyLayers = false;
  111. }
  112. else
  113. {
  114. _entity = EntityManager.SpawnEntity(_cfg.GetCVar(CCVars.TippyEntity), MapCoordinates.Nullspace);
  115. tippy.ModifyLayers = true;
  116. }
  117. if (!EntityManager.TryGetComponent(_entity, out sprite))
  118. return;
  119. if (!EntityManager.HasComponent<PaperVisualsComponent>(_entity))
  120. {
  121. var paper = EntityManager.AddComponent<PaperVisualsComponent>(_entity);
  122. paper.BackgroundImagePath = "/Textures/Interface/Paper/paper_background_default.svg.96dpi.png";
  123. paper.BackgroundPatchMargin = new(16f, 16f, 16f, 16f);
  124. paper.BackgroundModulate = new(255, 255, 204);
  125. paper.FontAccentColor = new(0, 0, 0);
  126. }
  127. tippy.InitLabel(EntityManager.GetComponentOrNull<PaperVisualsComponent>(_entity), _resCache);
  128. var scale = sprite.Scale;
  129. if (tippy.ModifyLayers)
  130. {
  131. sprite.Scale = Vector2.One;
  132. }
  133. else
  134. {
  135. sprite.Scale = new Vector2(3, 3);
  136. }
  137. tippy.Entity.SetEntity(_entity);
  138. tippy.Entity.Scale = scale;
  139. _currentMessage = next;
  140. _secondsUntilNextState = next.SlideTime;
  141. tippy.State = TippyState.Revealing;
  142. _previousStep = 0;
  143. if (tippy.ModifyLayers)
  144. {
  145. sprite.LayerSetAnimationTime("revealing", 0);
  146. sprite.LayerSetVisible("revealing", true);
  147. sprite.LayerSetVisible("speaking", false);
  148. sprite.LayerSetVisible("hiding", false);
  149. }
  150. sprite.Rotation = 0;
  151. tippy.Label.SetMarkupPermissive(_currentMessage.Msg);
  152. tippy.Label.Visible = false;
  153. tippy.LabelPanel.Visible = false;
  154. tippy.Visible = true;
  155. sprite.Visible = true;
  156. break;
  157. case TippyState.Revealing:
  158. tippy.State = TippyState.Speaking;
  159. if (!EntityManager.TryGetComponent(_entity, out sprite))
  160. return;
  161. sprite.Rotation = 0;
  162. _previousStep = 0;
  163. if (tippy.ModifyLayers)
  164. {
  165. sprite.LayerSetAnimationTime("speaking", 0);
  166. sprite.LayerSetVisible("revealing", false);
  167. sprite.LayerSetVisible("speaking", true);
  168. sprite.LayerSetVisible("hiding", false);
  169. }
  170. tippy.Label.Visible = true;
  171. tippy.LabelPanel.Visible = true;
  172. tippy.InvalidateArrange();
  173. tippy.InvalidateMeasure();
  174. if (_currentMessage != null)
  175. _secondsUntilNextState = _currentMessage.SpeakTime;
  176. break;
  177. case TippyState.Speaking:
  178. tippy.State = TippyState.Hiding;
  179. if (!EntityManager.TryGetComponent(_entity, out sprite))
  180. return;
  181. if (tippy.ModifyLayers)
  182. {
  183. sprite.LayerSetAnimationTime("hiding", 0);
  184. sprite.LayerSetVisible("revealing", false);
  185. sprite.LayerSetVisible("speaking", false);
  186. sprite.LayerSetVisible("hiding", true);
  187. }
  188. tippy.LabelPanel.Visible = false;
  189. if (_currentMessage != null)
  190. _secondsUntilNextState = _currentMessage.SlideTime;
  191. break;
  192. default: // finished hiding
  193. EntityManager.DeleteEntity(_entity);
  194. _entity = default;
  195. tippy.Visible = false;
  196. _currentMessage = null;
  197. _secondsUntilNextState = 0;
  198. tippy.State = TippyState.Hidden;
  199. break;
  200. }
  201. }
  202. private void OnScreenChanged((UIScreen? Old, UIScreen? New) ev)
  203. {
  204. ev.Old?.RemoveWidget<TippyUI>();
  205. _currentMessage = null;
  206. EntityManager.DeleteEntity(_entity);
  207. }
  208. }