using Content.Server.Objectives.Components; using Content.Shared.Mind; using Content.Shared.Objectives.Components; using Content.Shared.Roles; // DeltaV using Content.Shared.Roles.Jobs; // DeltaV using Content.Server.GameTicking.Rules; using Content.Server.Revolutionary.Components; using Robust.Shared.Prototypes; // DeltaV using Robust.Shared.Random; using System.Linq; namespace Content.Server.Objectives.Systems; /// /// Handles assinging a target to an objective entity with using different components. /// These can be combined with condition components for objective completions in order to create a variety of objectives. /// public sealed class PickObjectiveTargetSystem : EntitySystem { [Dependency] private readonly TargetObjectiveSystem _target = default!; [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly SharedRoleSystem _role = default!; // DeltaV [Dependency] private readonly IPrototypeManager _proto = default!; // DeltaV [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly TraitorRuleSystem _traitorRule = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnSpecificPersonAssigned); SubscribeLocalEvent(OnRandomPersonAssigned); SubscribeLocalEvent(OnRandomHeadAssigned); SubscribeLocalEvent(OnRandomTraitorProgressAssigned); SubscribeLocalEvent(OnRandomTraitorAliveAssigned); } private void OnSpecificPersonAssigned(Entity ent, ref ObjectiveAssignedEvent args) { // invalid objective prototype if (!TryComp(ent.Owner, out var target)) { args.Cancelled = true; return; } // target already assigned if (target.Target != null) return; if (args.Mind.OwnedEntity == null) { args.Cancelled = true; return; } var user = args.Mind.OwnedEntity.Value; if (!TryComp(user, out var targetComp) || targetComp.Target == null) { args.Cancelled = true; return; } _target.SetTarget(ent.Owner, targetComp.Target.Value); } private void OnRandomPersonAssigned(Entity ent, ref ObjectiveAssignedEvent args) { // Begin DeltaV Changes - replaced copy pasta with this Predicate pred = ent.Comp.OnlyChoosableJobs ? mindId => _role.MindHasRole(mindId, out var role) && role?.Comp1.JobPrototype is {} jobId && _proto.Index(jobId).SetPreference : _ => true; AssignRandomTarget(ent, ref args, pred); // End DeltaV Changes - replaced copy pasta with this } private void OnRandomHeadAssigned(Entity ent, ref ObjectiveAssignedEvent args) { // Begin DeltaV Changes - replaced copy pasta with this AssignRandomTarget(ent, ref args, mindId => TryComp(mindId, out var mind) && mind.OwnedEntity is { } ownedEnt && HasComp(ownedEnt)); // End DeltaV Changes - replaced copy pasta with this } /// /// DeltaV - Common code deduplicated from above functions. /// Filters all alive humans and picks a target from them. /// public void AssignRandomTarget(EntityUid uid, ref ObjectiveAssignedEvent args, Predicate filter, bool fallbackToAny = true) { // invalid prototype if (!TryComp(uid, out var target)) { args.Cancelled = true; return; } // target already assigned if (target.Target != null) return; // Get all alive humans, filter out any with TargetObjectiveImmuneComponent var allHumans = _mind.GetAliveHumans(args.MindId) .Where(mindId => { if (!TryComp(mindId, out var mindComp) || mindComp.OwnedEntity == null) return false; return !HasComp(mindComp.OwnedEntity.Value); }) .ToList(); // Can't have multiple objectives to kill the same person foreach (var objective in args.Mind.Objectives) { if (HasComp(objective) && TryComp(objective, out var kill)) { allHumans.RemoveAll(x => x.Owner == kill.Target); } } // Begin DeltaV Additions - no being asked to kill someone at centcom or whatever if (args.Mind.OwnedEntity is {} mob) { var map = Transform(mob).MapID; allHumans.RemoveAll(mindId => { if (Comp(mindId).OwnedEntity is {} otherMob) return Transform(otherMob).MapID != map; return false; }); } // End DeltaV Additions // 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; } // Pick between humans matching our filter or fall back to all humans alive var selectedHumans = filteredHumans.Count > 0 ? filteredHumans : allHumans; // Still no valid targets even after the fallback if (selectedHumans.Count == 0) { args.Cancelled = true; return; } _target.SetTarget(uid, _random.Pick(selectedHumans), target); } private void OnRandomTraitorProgressAssigned(Entity ent, ref ObjectiveAssignedEvent args) { // invalid prototype if (!TryComp(ent.Owner, out var target)) { args.Cancelled = true; return; } var traitors = _traitorRule.GetOtherTraitorMindsAliveAndConnected(args.Mind).ToHashSet(); // cant help anyone who is tasked with helping: // 1. thats boring // 2. no cyclic progress dependencies!!! foreach (var traitor in traitors) { // TODO: replace this with TryComp(traitor) or something when objectives are moved out of mind if (!TryComp(traitor.Id, out var mind)) continue; foreach (var objective in mind.Objectives) { if (HasComp(objective)) traitors.RemoveWhere(x => x.Mind == mind); } } // Can't have multiple objectives to help/save the same person foreach (var objective in args.Mind.Objectives) { if (HasComp(objective) || HasComp(objective)) { if (TryComp(objective, out var help)) { traitors.RemoveWhere(x => x.Id == help.Target); } } } // no more helpable traitors if (traitors.Count == 0) { args.Cancelled = true; return; } _target.SetTarget(ent.Owner, _random.Pick(traitors).Id, target); } private void OnRandomTraitorAliveAssigned(Entity ent, ref ObjectiveAssignedEvent args) { // invalid prototype if (!TryComp(ent.Owner, out var target)) { args.Cancelled = true; return; } var traitors = _traitorRule.GetOtherTraitorMindsAliveAndConnected(args.Mind).ToHashSet(); // Can't have multiple objectives to help/save the same person foreach (var objective in args.Mind.Objectives) { if (HasComp(objective) || HasComp(objective)) { if (TryComp(objective, out var help)) { traitors.RemoveWhere(x => x.Id == help.Target); } } } // You are the first/only traitor. if (traitors.Count == 0) { args.Cancelled = true; return; } _target.SetTarget(ent.Owner, _random.Pick(traitors).Id, target); } }