1
0

DamageForceSaySystem.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. using Content.Shared.Bed.Sleep;
  2. using Content.Shared.Damage;
  3. using Content.Shared.Damage.Events;
  4. using Content.Shared.Damage.ForceSay;
  5. using Content.Shared.FixedPoint;
  6. using Content.Shared.Mobs;
  7. using Content.Shared.Mobs.Systems;
  8. using Content.Shared.Stunnable;
  9. using Robust.Shared.Player;
  10. using Robust.Shared.Prototypes;
  11. using Robust.Shared.Random;
  12. using Robust.Shared.Timing;
  13. namespace Content.Server.Damage.ForceSay;
  14. /// <inheritdoc cref="DamageForceSayComponent"/>
  15. public sealed class DamageForceSaySystem : EntitySystem
  16. {
  17. [Dependency] private readonly IGameTiming _timing = default!;
  18. [Dependency] private readonly IPrototypeManager _prototype = default!;
  19. [Dependency] private readonly IRobustRandom _random = default!;
  20. public override void Initialize()
  21. {
  22. base.Initialize();
  23. SubscribeLocalEvent<DamageForceSayComponent, StunnedEvent>(OnStunned);
  24. SubscribeLocalEvent<DamageForceSayComponent, MobStateChangedEvent>(OnMobStateChanged);
  25. // need to raise after mobthreshold
  26. // so that we don't accidentally raise one for damage before one for mobstate
  27. // (this won't double raise, because of the cooldown)
  28. SubscribeLocalEvent<DamageForceSayComponent, DamageChangedEvent>(OnDamageChanged, after: new []{ typeof(MobThresholdSystem)} );
  29. SubscribeLocalEvent<DamageForceSayComponent, SleepStateChangedEvent>(OnSleep);
  30. }
  31. public override void Update(float frameTime)
  32. {
  33. base.Update(frameTime);
  34. var query = AllEntityQuery<AllowNextCritSpeechComponent>();
  35. while (query.MoveNext(out var uid, out var comp))
  36. {
  37. if (_timing.CurTime < comp.Timeout)
  38. continue;
  39. RemCompDeferred<AllowNextCritSpeechComponent>(uid);
  40. }
  41. }
  42. private void TryForceSay(EntityUid uid, DamageForceSayComponent component, bool useSuffix=true)
  43. {
  44. if (!TryComp<ActorComponent>(uid, out var actor))
  45. return;
  46. // disallow if cooldown hasn't ended
  47. if (component.NextAllowedTime != null &&
  48. _timing.CurTime < component.NextAllowedTime)
  49. return;
  50. var ev = new BeforeForceSayEvent(component.ForceSayStringDataset);
  51. RaiseLocalEvent(uid, ev);
  52. if (!_prototype.TryIndex(ev.Prefix, out var prefixList))
  53. return;
  54. var suffix = Loc.GetString(_random.Pick(prefixList.Values));
  55. // set cooldown & raise event
  56. component.NextAllowedTime = _timing.CurTime + component.Cooldown;
  57. RaiseNetworkEvent(new DamageForceSayEvent { Suffix = useSuffix ? suffix : null }, actor.PlayerSession);
  58. }
  59. private void AllowNextSpeech(EntityUid uid)
  60. {
  61. if (!TryComp<ActorComponent>(uid, out var actor))
  62. return;
  63. var nextCrit = EnsureComp<AllowNextCritSpeechComponent>(uid);
  64. // timeout is *3 ping to compensate for roundtrip + leeway
  65. nextCrit.Timeout = _timing.CurTime + TimeSpan.FromMilliseconds(actor.PlayerSession.Ping * 3);
  66. }
  67. private void OnSleep(EntityUid uid, DamageForceSayComponent component, SleepStateChangedEvent args)
  68. {
  69. if (!args.FellAsleep)
  70. return;
  71. TryForceSay(uid, component);
  72. AllowNextSpeech(uid);
  73. }
  74. private void OnStunned(EntityUid uid, DamageForceSayComponent component, ref StunnedEvent args)
  75. {
  76. TryForceSay(uid, component);
  77. }
  78. private void OnDamageChanged(EntityUid uid, DamageForceSayComponent component, DamageChangedEvent args)
  79. {
  80. if (args.DamageDelta == null || !args.DamageIncreased || args.DamageDelta.GetTotal() < component.DamageThreshold)
  81. return;
  82. if (component.ValidDamageGroups != null)
  83. {
  84. var totalApplicableDamage = FixedPoint2.Zero;
  85. foreach (var (group, value) in args.DamageDelta.GetDamagePerGroup(_prototype))
  86. {
  87. if (!component.ValidDamageGroups.Contains(group))
  88. continue;
  89. totalApplicableDamage += value;
  90. }
  91. if (totalApplicableDamage < component.DamageThreshold)
  92. return;
  93. }
  94. TryForceSay(uid, component);
  95. }
  96. private void OnMobStateChanged(EntityUid uid, DamageForceSayComponent component, MobStateChangedEvent args)
  97. {
  98. if (args is not { OldMobState: MobState.Alive, NewMobState: MobState.Critical or MobState.Dead })
  99. return;
  100. // no suffix for the drama
  101. // LING IN MAI-
  102. TryForceSay(uid, component, false);
  103. AllowNextSpeech(uid);
  104. }
  105. }