1
0

PickObjectiveTargetSystem.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. using Content.Server.Objectives.Components;
  2. using Content.Shared.Mind;
  3. using Content.Shared.Objectives.Components;
  4. using Content.Server.GameTicking.Rules;
  5. using Content.Server.Revolutionary.Components;
  6. using Robust.Shared.Random;
  7. using System.Linq;
  8. namespace Content.Server.Objectives.Systems;
  9. /// <summary>
  10. /// Handles assinging a target to an objective entity with <see cref="TargetObjectiveComponent"/> using different components.
  11. /// These can be combined with condition components for objective completions in order to create a variety of objectives.
  12. /// </summary>
  13. public sealed class PickObjectiveTargetSystem : EntitySystem
  14. {
  15. [Dependency] private readonly TargetObjectiveSystem _target = default!;
  16. [Dependency] private readonly SharedMindSystem _mind = default!;
  17. [Dependency] private readonly IRobustRandom _random = default!;
  18. [Dependency] private readonly TraitorRuleSystem _traitorRule = default!;
  19. public override void Initialize()
  20. {
  21. base.Initialize();
  22. SubscribeLocalEvent<PickSpecificPersonComponent, ObjectiveAssignedEvent>(OnSpecificPersonAssigned);
  23. SubscribeLocalEvent<PickRandomPersonComponent, ObjectiveAssignedEvent>(OnRandomPersonAssigned);
  24. SubscribeLocalEvent<PickRandomHeadComponent, ObjectiveAssignedEvent>(OnRandomHeadAssigned);
  25. SubscribeLocalEvent<RandomTraitorProgressComponent, ObjectiveAssignedEvent>(OnRandomTraitorProgressAssigned);
  26. SubscribeLocalEvent<RandomTraitorAliveComponent, ObjectiveAssignedEvent>(OnRandomTraitorAliveAssigned);
  27. }
  28. private void OnSpecificPersonAssigned(Entity<PickSpecificPersonComponent> ent, ref ObjectiveAssignedEvent args)
  29. {
  30. // invalid objective prototype
  31. if (!TryComp<TargetObjectiveComponent>(ent.Owner, out var target))
  32. {
  33. args.Cancelled = true;
  34. return;
  35. }
  36. // target already assigned
  37. if (target.Target != null)
  38. return;
  39. if (args.Mind.OwnedEntity == null)
  40. {
  41. args.Cancelled = true;
  42. return;
  43. }
  44. var user = args.Mind.OwnedEntity.Value;
  45. if (!TryComp<TargetOverrideComponent>(user, out var targetComp) || targetComp.Target == null)
  46. {
  47. args.Cancelled = true;
  48. return;
  49. }
  50. _target.SetTarget(ent.Owner, targetComp.Target.Value);
  51. }
  52. private void OnRandomPersonAssigned(Entity<PickRandomPersonComponent> ent, ref ObjectiveAssignedEvent args)
  53. {
  54. // invalid objective prototype
  55. if (!TryComp<TargetObjectiveComponent>(ent.Owner, out var target))
  56. {
  57. args.Cancelled = true;
  58. return;
  59. }
  60. // target already assigned
  61. if (target.Target != null)
  62. return;
  63. var allHumans = _mind.GetAliveHumans(args.MindId);
  64. // Can't have multiple objectives to kill the same person
  65. foreach (var objective in args.Mind.Objectives)
  66. {
  67. if (HasComp<KillPersonConditionComponent>(objective) && TryComp<TargetObjectiveComponent>(objective, out var kill))
  68. {
  69. allHumans.RemoveWhere(x => x.Owner == kill.Target);
  70. }
  71. }
  72. // no other humans to kill
  73. if (allHumans.Count == 0)
  74. {
  75. args.Cancelled = true;
  76. return;
  77. }
  78. _target.SetTarget(ent.Owner, _random.Pick(allHumans), target);
  79. }
  80. private void OnRandomHeadAssigned(Entity<PickRandomHeadComponent> ent, ref ObjectiveAssignedEvent args)
  81. {
  82. // invalid prototype
  83. if (!TryComp<TargetObjectiveComponent>(ent.Owner, out var target))
  84. {
  85. args.Cancelled = true;
  86. return;
  87. }
  88. // target already assigned
  89. if (target.Target != null)
  90. return;
  91. // no other humans to kill
  92. var allHumans = _mind.GetAliveHumans(args.MindId);
  93. if (allHumans.Count == 0)
  94. {
  95. args.Cancelled = true;
  96. return;
  97. }
  98. var allHeads = new HashSet<Entity<MindComponent>>();
  99. foreach (var person in allHumans)
  100. {
  101. if (TryComp<MindComponent>(person, out var mind) && mind.OwnedEntity is { } owned && HasComp<CommandStaffComponent>(owned))
  102. allHeads.Add(person);
  103. }
  104. if (allHeads.Count == 0)
  105. allHeads = allHumans; // fallback to non-head target
  106. _target.SetTarget(ent.Owner, _random.Pick(allHeads), target);
  107. }
  108. private void OnRandomTraitorProgressAssigned(Entity<RandomTraitorProgressComponent> ent, ref ObjectiveAssignedEvent args)
  109. {
  110. // invalid prototype
  111. if (!TryComp<TargetObjectiveComponent>(ent.Owner, out var target))
  112. {
  113. args.Cancelled = true;
  114. return;
  115. }
  116. var traitors = _traitorRule.GetOtherTraitorMindsAliveAndConnected(args.Mind).ToHashSet();
  117. // cant help anyone who is tasked with helping:
  118. // 1. thats boring
  119. // 2. no cyclic progress dependencies!!!
  120. foreach (var traitor in traitors)
  121. {
  122. // TODO: replace this with TryComp<ObjectivesComponent>(traitor) or something when objectives are moved out of mind
  123. if (!TryComp<MindComponent>(traitor.Id, out var mind))
  124. continue;
  125. foreach (var objective in mind.Objectives)
  126. {
  127. if (HasComp<HelpProgressConditionComponent>(objective))
  128. traitors.RemoveWhere(x => x.Mind == mind);
  129. }
  130. }
  131. // Can't have multiple objectives to help/save the same person
  132. foreach (var objective in args.Mind.Objectives)
  133. {
  134. if (HasComp<RandomTraitorAliveComponent>(objective) || HasComp<RandomTraitorProgressComponent>(objective))
  135. {
  136. if (TryComp<TargetObjectiveComponent>(objective, out var help))
  137. {
  138. traitors.RemoveWhere(x => x.Id == help.Target);
  139. }
  140. }
  141. }
  142. // no more helpable traitors
  143. if (traitors.Count == 0)
  144. {
  145. args.Cancelled = true;
  146. return;
  147. }
  148. _target.SetTarget(ent.Owner, _random.Pick(traitors).Id, target);
  149. }
  150. private void OnRandomTraitorAliveAssigned(Entity<RandomTraitorAliveComponent> ent, ref ObjectiveAssignedEvent args)
  151. {
  152. // invalid prototype
  153. if (!TryComp<TargetObjectiveComponent>(ent.Owner, out var target))
  154. {
  155. args.Cancelled = true;
  156. return;
  157. }
  158. var traitors = _traitorRule.GetOtherTraitorMindsAliveAndConnected(args.Mind).ToHashSet();
  159. // Can't have multiple objectives to help/save the same person
  160. foreach (var objective in args.Mind.Objectives)
  161. {
  162. if (HasComp<RandomTraitorAliveComponent>(objective) || HasComp<RandomTraitorProgressComponent>(objective))
  163. {
  164. if (TryComp<TargetObjectiveComponent>(objective, out var help))
  165. {
  166. traitors.RemoveWhere(x => x.Id == help.Target);
  167. }
  168. }
  169. }
  170. // You are the first/only traitor.
  171. if (traitors.Count == 0)
  172. {
  173. args.Cancelled = true;
  174. return;
  175. }
  176. _target.SetTarget(ent.Owner, _random.Pick(traitors).Id, target);
  177. }
  178. }