TipsSystem.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. using Content.Server.Chat.Managers;
  2. using Content.Server.GameTicking;
  3. using Content.Shared.CCVar;
  4. using Content.Shared.Chat;
  5. using Content.Shared.Dataset;
  6. using Content.Shared.Tips;
  7. using Robust.Server.GameObjects;
  8. using Robust.Server.Player;
  9. using Robust.Shared.Configuration;
  10. using Robust.Shared.Console;
  11. using Robust.Shared.Player;
  12. using Robust.Shared.Prototypes;
  13. using Robust.Shared.Random;
  14. using Robust.Shared.Timing;
  15. namespace Content.Server.Tips;
  16. /// <summary>
  17. /// Handles periodically displaying gameplay tips to all players ingame.
  18. /// </summary>
  19. public sealed class TipsSystem : EntitySystem
  20. {
  21. [Dependency] private readonly IChatManager _chat = default!;
  22. [Dependency] private readonly IPrototypeManager _prototype = default!;
  23. [Dependency] private readonly IConfigurationManager _cfg = default!;
  24. [Dependency] private readonly IGameTiming _timing = default!;
  25. [Dependency] private readonly IRobustRandom _random = default!;
  26. [Dependency] private readonly GameTicker _ticker = default!;
  27. [Dependency] private readonly IConsoleHost _conHost = default!;
  28. [Dependency] private readonly IPlayerManager _playerManager = default!;
  29. private bool _tipsEnabled;
  30. private float _tipTimeOutOfRound;
  31. private float _tipTimeInRound;
  32. private string _tipsDataset = "";
  33. private float _tipTippyChance;
  34. [ViewVariables(VVAccess.ReadWrite)]
  35. private TimeSpan _nextTipTime = TimeSpan.Zero;
  36. public override void Initialize()
  37. {
  38. base.Initialize();
  39. SubscribeLocalEvent<GameRunLevelChangedEvent>(OnGameRunLevelChanged);
  40. Subs.CVar(_cfg, CCVars.TipFrequencyOutOfRound, SetOutOfRound, true);
  41. Subs.CVar(_cfg, CCVars.TipFrequencyInRound, SetInRound, true);
  42. Subs.CVar(_cfg, CCVars.TipsEnabled, SetEnabled, true);
  43. Subs.CVar(_cfg, CCVars.TipsDataset, SetDataset, true);
  44. Subs.CVar(_cfg, CCVars.TipsTippyChance, SetTippyChance, true);
  45. RecalculateNextTipTime();
  46. _conHost.RegisterCommand("tippy", Loc.GetString("cmd-tippy-desc"), Loc.GetString("cmd-tippy-help"), SendTippy, SendTippyHelper);
  47. _conHost.RegisterCommand("tip", Loc.GetString("cmd-tip-desc"), "tip", SendTip);
  48. }
  49. private CompletionResult SendTippyHelper(IConsoleShell shell, string[] args)
  50. {
  51. return args.Length switch
  52. {
  53. 1 => CompletionResult.FromHintOptions(CompletionHelper.SessionNames(), Loc.GetString("cmd-tippy-auto-1")),
  54. 2 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-2")),
  55. 3 => CompletionResult.FromHintOptions(CompletionHelper.PrototypeIDs<EntityPrototype>(), Loc.GetString("cmd-tippy-auto-3")),
  56. 4 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-4")),
  57. 5 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-5")),
  58. 6 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-6")),
  59. _ => CompletionResult.Empty
  60. };
  61. }
  62. private void SendTip(IConsoleShell shell, string argstr, string[] args)
  63. {
  64. AnnounceRandomTip();
  65. RecalculateNextTipTime();
  66. }
  67. private void SendTippy(IConsoleShell shell, string argstr, string[] args)
  68. {
  69. if (args.Length < 2)
  70. {
  71. shell.WriteLine(Loc.GetString("cmd-tippy-help"));
  72. return;
  73. }
  74. ActorComponent? actor = null;
  75. if (args[0] != "all")
  76. {
  77. ICommonSession? session;
  78. if (args.Length > 0)
  79. {
  80. // Get player entity
  81. if (!_playerManager.TryGetSessionByUsername(args[0], out session))
  82. {
  83. shell.WriteLine(Loc.GetString("cmd-tippy-error-no-user"));
  84. return;
  85. }
  86. }
  87. else
  88. {
  89. session = shell.Player;
  90. }
  91. if (session?.AttachedEntity is not { } user)
  92. {
  93. shell.WriteLine(Loc.GetString("cmd-tippy-error-no-user"));
  94. return;
  95. }
  96. if (!TryComp(user, out actor))
  97. {
  98. shell.WriteError(Loc.GetString("cmd-tippy-error-no-user"));
  99. return;
  100. }
  101. }
  102. var ev = new TippyEvent(args[1]);
  103. if (args.Length > 2)
  104. {
  105. ev.Proto = args[2];
  106. if (!_prototype.HasIndex<EntityPrototype>(args[2]))
  107. {
  108. shell.WriteError(Loc.GetString("cmd-tippy-error-no-prototype", ("proto", args[2])));
  109. return;
  110. }
  111. }
  112. if (args.Length > 3)
  113. ev.SpeakTime = float.Parse(args[3]);
  114. if (args.Length > 4)
  115. ev.SlideTime = float.Parse(args[4]);
  116. if (args.Length > 5)
  117. ev.WaddleInterval = float.Parse(args[5]);
  118. if (actor != null)
  119. RaiseNetworkEvent(ev, actor.PlayerSession);
  120. else
  121. RaiseNetworkEvent(ev);
  122. }
  123. public override void Update(float frameTime)
  124. {
  125. base.Update(frameTime);
  126. if (!_tipsEnabled)
  127. return;
  128. if (_nextTipTime != TimeSpan.Zero && _timing.CurTime > _nextTipTime)
  129. {
  130. AnnounceRandomTip();
  131. RecalculateNextTipTime();
  132. }
  133. }
  134. private void SetOutOfRound(float value)
  135. {
  136. _tipTimeOutOfRound = value;
  137. }
  138. private void SetInRound(float value)
  139. {
  140. _tipTimeInRound = value;
  141. }
  142. private void SetEnabled(bool value)
  143. {
  144. _tipsEnabled = value;
  145. if (_nextTipTime != TimeSpan.Zero)
  146. RecalculateNextTipTime();
  147. }
  148. private void SetDataset(string value)
  149. {
  150. _tipsDataset = value;
  151. }
  152. private void SetTippyChance(float value)
  153. {
  154. _tipTippyChance = value;
  155. }
  156. private void AnnounceRandomTip()
  157. {
  158. if (!_prototype.TryIndex<LocalizedDatasetPrototype>(_tipsDataset, out var tips))
  159. return;
  160. var tip = _random.Pick(tips.Values);
  161. var msg = Loc.GetString("tips-system-chat-message-wrap", ("tip", Loc.GetString(tip)));
  162. if (_random.Prob(_tipTippyChance))
  163. {
  164. var ev = new TippyEvent(msg);
  165. ev.SpeakTime = 1 + tip.Length * 0.05f;
  166. RaiseNetworkEvent(ev);
  167. } else
  168. {
  169. _chat.ChatMessageToManyFiltered(Filter.Broadcast(), ChatChannel.OOC, tip, msg,
  170. EntityUid.Invalid, false, false, Color.MediumPurple);
  171. }
  172. }
  173. private void RecalculateNextTipTime()
  174. {
  175. if (_ticker.RunLevel == GameRunLevel.InRound)
  176. {
  177. _nextTipTime = _timing.CurTime + TimeSpan.FromSeconds(_tipTimeInRound);
  178. }
  179. else
  180. {
  181. _nextTipTime = _timing.CurTime + TimeSpan.FromSeconds(_tipTimeOutOfRound);
  182. }
  183. }
  184. private void OnGameRunLevelChanged(GameRunLevelChangedEvent ev)
  185. {
  186. // reset for lobby -> inround
  187. // reset for inround -> post but not post -> lobby
  188. if (ev.New == GameRunLevel.InRound || ev.Old == GameRunLevel.InRound)
  189. {
  190. RecalculateNextTipTime();
  191. }
  192. }
  193. }