TriggerSystem.Voice.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. using Content.Server.Explosion.Components;
  2. using Content.Server.Speech;
  3. using Content.Server.Speech.Components;
  4. using Content.Shared.Database;
  5. using Content.Shared.Examine;
  6. using Content.Shared.Verbs;
  7. namespace Content.Server.Explosion.EntitySystems
  8. {
  9. public sealed partial class TriggerSystem
  10. {
  11. private void InitializeVoice()
  12. {
  13. SubscribeLocalEvent<TriggerOnVoiceComponent, ComponentInit>(OnVoiceInit);
  14. SubscribeLocalEvent<TriggerOnVoiceComponent, ExaminedEvent>(OnVoiceExamine);
  15. SubscribeLocalEvent<TriggerOnVoiceComponent, GetVerbsEvent<AlternativeVerb>>(OnVoiceGetAltVerbs);
  16. SubscribeLocalEvent<TriggerOnVoiceComponent, ListenEvent>(OnListen);
  17. }
  18. private void OnVoiceInit(EntityUid uid, TriggerOnVoiceComponent component, ComponentInit args)
  19. {
  20. if (component.IsListening)
  21. EnsureComp<ActiveListenerComponent>(uid).Range = component.ListenRange;
  22. else
  23. RemCompDeferred<ActiveListenerComponent>(uid);
  24. }
  25. private void OnListen(Entity<TriggerOnVoiceComponent> ent, ref ListenEvent args)
  26. {
  27. var component = ent.Comp;
  28. var message = args.Message.Trim();
  29. if (component.IsRecording)
  30. {
  31. var ev = new ListenAttemptEvent(args.Source);
  32. RaiseLocalEvent(ent, ev);
  33. if (ev.Cancelled)
  34. return;
  35. if (message.Length >= component.MinLength && message.Length <= component.MaxLength)
  36. FinishRecording(ent, args.Source, args.Message);
  37. else if (message.Length > component.MaxLength)
  38. _popupSystem.PopupEntity(Loc.GetString("popup-trigger-voice-record-failed-too-long"), ent);
  39. else if (message.Length < component.MinLength)
  40. _popupSystem.PopupEntity(Loc.GetString("popup-trigger-voice-record-failed-too-short"), ent);
  41. return;
  42. }
  43. if (!string.IsNullOrWhiteSpace(component.KeyPhrase) && message.Contains(component.KeyPhrase, StringComparison.InvariantCultureIgnoreCase))
  44. {
  45. _adminLogger.Add(LogType.Trigger, LogImpact.High,
  46. $"A voice-trigger on {ToPrettyString(ent):entity} was triggered by {ToPrettyString(args.Source):speaker} speaking the key-phrase {component.KeyPhrase}.");
  47. Trigger(ent, args.Source);
  48. var voice = new VoiceTriggeredEvent(args.Source, message);
  49. RaiseLocalEvent(ent, ref voice);
  50. }
  51. }
  52. private void OnVoiceGetAltVerbs(Entity<TriggerOnVoiceComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
  53. {
  54. if (!args.CanInteract || !args.CanAccess)
  55. return;
  56. var component = ent.Comp;
  57. var @event = args;
  58. args.Verbs.Add(new AlternativeVerb()
  59. {
  60. Text = Loc.GetString(component.IsRecording ? "verb-trigger-voice-stop" : "verb-trigger-voice-record"),
  61. Act = () =>
  62. {
  63. if (component.IsRecording)
  64. StopRecording(ent);
  65. else
  66. StartRecording(ent, @event.User);
  67. },
  68. Priority = 1
  69. });
  70. if (string.IsNullOrWhiteSpace(component.KeyPhrase))
  71. return;
  72. args.Verbs.Add(new AlternativeVerb()
  73. {
  74. Text = Loc.GetString("verb-trigger-voice-clear"),
  75. Act = () =>
  76. {
  77. component.KeyPhrase = null;
  78. component.IsRecording = false;
  79. RemComp<ActiveListenerComponent>(ent);
  80. }
  81. });
  82. }
  83. public void StartRecording(Entity<TriggerOnVoiceComponent> ent, EntityUid user)
  84. {
  85. var component = ent.Comp;
  86. component.IsRecording = true;
  87. EnsureComp<ActiveListenerComponent>(ent).Range = component.ListenRange;
  88. _adminLogger.Add(LogType.Trigger, LogImpact.Low,
  89. $"A voice-trigger on {ToPrettyString(ent):entity} has started recording. User: {ToPrettyString(user):user}");
  90. _popupSystem.PopupEntity(Loc.GetString("popup-trigger-voice-start-recording"), ent);
  91. }
  92. public void StopRecording(Entity<TriggerOnVoiceComponent> ent)
  93. {
  94. var component = ent.Comp;
  95. component.IsRecording = false;
  96. if (string.IsNullOrWhiteSpace(component.KeyPhrase))
  97. RemComp<ActiveListenerComponent>(ent);
  98. _popupSystem.PopupEntity(Loc.GetString("popup-trigger-voice-stop-recording"), ent);
  99. }
  100. public void FinishRecording(Entity<TriggerOnVoiceComponent> ent, EntityUid source, string message)
  101. {
  102. var component = ent.Comp;
  103. component.KeyPhrase = message;
  104. component.IsRecording = false;
  105. _adminLogger.Add(LogType.Trigger, LogImpact.Low,
  106. $"A voice-trigger on {ToPrettyString(ent):entity} has recorded a new keyphrase: '{component.KeyPhrase}'. Recorded from {ToPrettyString(source):speaker}");
  107. _popupSystem.PopupEntity(Loc.GetString("popup-trigger-voice-recorded", ("keyphrase", component.KeyPhrase!)), ent);
  108. }
  109. private void OnVoiceExamine(EntityUid uid, TriggerOnVoiceComponent component, ExaminedEvent args)
  110. {
  111. if (args.IsInDetailsRange)
  112. {
  113. args.PushText(string.IsNullOrWhiteSpace(component.KeyPhrase)
  114. ? Loc.GetString("trigger-voice-uninitialized")
  115. : Loc.GetString("examine-trigger-voice", ("keyphrase", component.KeyPhrase)));
  116. }
  117. }
  118. }
  119. }
  120. /// <summary>
  121. /// Raised when a voice trigger is activated, containing the message that triggered it.
  122. /// </summary>
  123. /// <param name="Source"> The EntityUid of the entity sending the message</param>
  124. /// <param name="Message"> The contents of the message</param>
  125. [ByRefEvent]
  126. public readonly record struct VoiceTriggeredEvent(EntityUid Source, string? Message);