diff --git a/Content.Server/Objectives/Components/TargetObjectiveImmuneComponent.cs b/Content.Server/Objectives/Components/TargetObjectiveImmuneComponent.cs new file mode 100644 index 00000000000..1f2bc961fd0 --- /dev/null +++ b/Content.Server/Objectives/Components/TargetObjectiveImmuneComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Server.Objectives.Components; + +/// +/// Use this to mark a player as immune to any target objectives, useful for ghost roles or events. +/// +[RegisterComponent] +public sealed partial class TargetObjectiveImmuneComponent : Component +{ +} diff --git a/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs b/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs index 8dcbf191b36..d61310908c3 100644 --- a/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs +++ b/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs @@ -1,3 +1,4 @@ +using System.Linq; using Content.Server.Objectives.Components; using Content.Server.Revolutionary.Components; using Content.Server.Shuttles.Systems; @@ -25,9 +26,7 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnGetProgress); - SubscribeLocalEvent(OnPersonAssigned); - SubscribeLocalEvent(OnHeadAssigned); } @@ -41,29 +40,15 @@ private void OnGetProgress(EntityUid uid, KillPersonConditionComponent comp, ref private void OnPersonAssigned(EntityUid uid, PickRandomPersonComponent comp, ref ObjectiveAssignedEvent args) { - // invalid objective prototype - if (!TryComp(uid, out var target)) - { - args.Cancelled = true; - return; - } - - // target already assigned - if (target.Target != null) - return; - - // no other humans to kill - var allHumans = _mind.GetAliveHumansExcept(args.MindId); - if (allHumans.Count == 0) - { - args.Cancelled = true; - return; - } - - _target.SetTarget(uid, _random.Pick(allHumans), target); + AssignRandomTarget(uid, args, _ => true); } private void OnHeadAssigned(EntityUid uid, PickRandomHeadComponent comp, ref ObjectiveAssignedEvent args) + { + AssignRandomTarget(uid, args, mind => HasComp(uid)); + } + + private void AssignRandomTarget(EntityUid uid, ObjectiveAssignedEvent args, Predicate filter, bool fallbackToAny = true) { // invalid prototype if (!TryComp(uid, out var target)) @@ -76,25 +61,30 @@ private void OnHeadAssigned(EntityUid uid, PickRandomHeadComponent comp, ref Obj if (target.Target != null) return; - // no other humans to kill - var allHumans = _mind.GetAliveHumansExcept(args.MindId); - if (allHumans.Count == 0) + // Get all alive humans, filter out any with TargetObjectiveImmuneComponent + var allHumans = _mind.GetAliveHumansExcept(args.MindId) + .Where(mindId => + { + if (!TryComp(mindId, out var mindComp) || mindComp.OwnedEntity == null) + return false; + return !HasComp(mindComp.OwnedEntity.Value); + }) + .ToList(); + + // Filter out targets based on the filter + var filteredHumans = allHumans.Where(mind => filter(mind)).ToList(); + + // There's no humans and we can't fall back to any other target + if (filteredHumans.Count == 0 && !fallbackToAny) { args.Cancelled = true; return; } - var allHeads = new List(); - foreach (var person in allHumans) - { - if (TryComp(person, out var mind) && mind.OwnedEntity is { } ent && HasComp(ent)) - allHeads.Add(person); - } - - if (allHeads.Count == 0) - allHeads = allHumans; // fallback to non-head target + // Pick between humans matching our filter or fall back to all humans alive + var selectedHumans = filteredHumans.Count > 0 ? filteredHumans : allHumans; - _target.SetTarget(uid, _random.Pick(allHeads), target); + _target.SetTarget(uid, _random.Pick(selectedHumans), target); } private float GetProgress(EntityUid target, bool requireDead)