diff --git a/Content.Server/Objectives/Components/PickRandomHeadComponent.cs b/Content.Server/Objectives/Components/PickRandomHeadComponent.cs index c2f82fb6c5..38ed252264 100644 --- a/Content.Server/Objectives/Components/PickRandomHeadComponent.cs +++ b/Content.Server/Objectives/Components/PickRandomHeadComponent.cs @@ -1,12 +1,8 @@ -using Content.Server.Objectives.Systems; - namespace Content.Server.Objectives.Components; /// /// Sets the target for to a random head. /// If there are no heads it will fallback to any person. /// -[RegisterComponent, Access(typeof(KillPersonConditionSystem))] -public sealed partial class PickRandomHeadComponent : Component -{ -} +[RegisterComponent] +public sealed partial class PickRandomHeadComponent : Component; diff --git a/Content.Server/Objectives/Components/PickRandomPersonComponent.cs b/Content.Server/Objectives/Components/PickRandomPersonComponent.cs index 4188b1da3d..bf4135e2a9 100644 --- a/Content.Server/Objectives/Components/PickRandomPersonComponent.cs +++ b/Content.Server/Objectives/Components/PickRandomPersonComponent.cs @@ -1,11 +1,7 @@ -using Content.Server.Objectives.Systems; - namespace Content.Server.Objectives.Components; /// /// Sets the target for to a random person. /// -[RegisterComponent, Access(typeof(KillPersonConditionSystem))] -public sealed partial class PickRandomPersonComponent : Component -{ -} +[RegisterComponent] +public sealed partial class PickRandomPersonComponent : Component; diff --git a/Content.Server/Objectives/Components/PickSpecificPersonComponent.cs b/Content.Server/Objectives/Components/PickSpecificPersonComponent.cs new file mode 100644 index 0000000000..03a482fc7b --- /dev/null +++ b/Content.Server/Objectives/Components/PickSpecificPersonComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Server.Objectives.Components; + +/// +/// Sets this objective's target to the one given in , if the entity has it. +/// This component needs to be added to objective entity itself. +/// +[RegisterComponent] +public sealed partial class PickSpecificPersonComponent : Component; diff --git a/Content.Server/Objectives/Components/RandomTraitorAliveComponent.cs b/Content.Server/Objectives/Components/RandomTraitorAliveComponent.cs index fd37d0d2c8..1c45cb45d5 100644 --- a/Content.Server/Objectives/Components/RandomTraitorAliveComponent.cs +++ b/Content.Server/Objectives/Components/RandomTraitorAliveComponent.cs @@ -1,11 +1,7 @@ -using Content.Server.Objectives.Systems; - namespace Content.Server.Objectives.Components; /// /// Sets the target for to a random traitor. /// -[RegisterComponent, Access(typeof(KeepAliveConditionSystem))] -public sealed partial class RandomTraitorAliveComponent : Component -{ -} +[RegisterComponent] +public sealed partial class RandomTraitorAliveComponent : Component; diff --git a/Content.Server/Objectives/Components/RandomTraitorProgressComponent.cs b/Content.Server/Objectives/Components/RandomTraitorProgressComponent.cs index c05ac0d3ef..f2da9778eb 100644 --- a/Content.Server/Objectives/Components/RandomTraitorProgressComponent.cs +++ b/Content.Server/Objectives/Components/RandomTraitorProgressComponent.cs @@ -1,11 +1,7 @@ -using Content.Server.Objectives.Systems; - namespace Content.Server.Objectives.Components; /// /// Sets the target for to a random traitor. /// -[RegisterComponent, Access(typeof(HelpProgressConditionSystem))] -public sealed partial class RandomTraitorProgressComponent : Component -{ -} +[RegisterComponent] +public sealed partial class RandomTraitorProgressComponent : Component; diff --git a/Content.Server/Objectives/Components/TargetOverrideComponent.cs b/Content.Server/Objectives/Components/TargetOverrideComponent.cs new file mode 100644 index 0000000000..faa402012c --- /dev/null +++ b/Content.Server/Objectives/Components/TargetOverrideComponent.cs @@ -0,0 +1,16 @@ +namespace Content.Server.Objectives.Components; + +/// +/// Sets a target objective to a specific target when receiving it. +/// The objective entity needs to have . +/// This component needs to be added to entity receiving the objective. +/// +[RegisterComponent] +public sealed partial class TargetOverrideComponent : Component +{ + /// + /// The entity that should be targeted. + /// + [DataField] + public EntityUid? Target; +} diff --git a/Content.Server/Objectives/Systems/HelpProgressConditionSystem.cs b/Content.Server/Objectives/Systems/HelpProgressConditionSystem.cs index 7639e69bfd..1816896ecc 100644 --- a/Content.Server/Objectives/Systems/HelpProgressConditionSystem.cs +++ b/Content.Server/Objectives/Systems/HelpProgressConditionSystem.cs @@ -1,31 +1,23 @@ -using Content.Server.GameTicking.Rules; using Content.Server.Objectives.Components; using Content.Shared.Mind; using Content.Shared.Objectives.Components; using Content.Shared.Objectives.Systems; -using Content.Shared.Roles.Jobs; -using Robust.Shared.Random; -using System.Linq; namespace Content.Server.Objectives.Systems; /// -/// Handles help progress condition logic and picking random help targets. +/// Handles help progress condition logic. /// public sealed class HelpProgressConditionSystem : EntitySystem { - [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedObjectivesSystem _objectives = default!; [Dependency] private readonly TargetObjectiveSystem _target = default!; - [Dependency] private readonly TraitorRuleSystem _traitorRule = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnGetProgress); - - SubscribeLocalEvent(OnTraitorAssigned); } private void OnGetProgress(EntityUid uid, HelpProgressConditionComponent comp, ref ObjectiveGetProgressEvent args) @@ -36,55 +28,6 @@ public sealed class HelpProgressConditionSystem : EntitySystem args.Progress = GetProgress(target.Value); } - private void OnTraitorAssigned(EntityUid uid, RandomTraitorProgressComponent comp, ref ObjectiveAssignedEvent args) - { - // invalid prototype - if (!TryComp(uid, 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(uid, _random.Pick(traitors).Id, target); - } - private float GetProgress(EntityUid target) { var total = 0f; // how much progress they have diff --git a/Content.Server/Objectives/Systems/KeepAliveCondition.cs b/Content.Server/Objectives/Systems/KeepAliveCondition.cs index fad8aa6d18..f68227e861 100644 --- a/Content.Server/Objectives/Systems/KeepAliveCondition.cs +++ b/Content.Server/Objectives/Systems/KeepAliveCondition.cs @@ -1,30 +1,22 @@ using Content.Server.Objectives.Components; -using Content.Server.GameTicking.Rules; using Content.Shared.Mind; using Content.Shared.Objectives.Components; -using Content.Shared.Roles.Jobs; -using Robust.Shared.Random; -using System.Linq; namespace Content.Server.Objectives.Systems; /// -/// Handles keep alive condition logic and picking random traitors to keep alive. +/// Handles keep alive condition logic. /// public sealed class KeepAliveConditionSystem : EntitySystem { - [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly TargetObjectiveSystem _target = default!; - [Dependency] private readonly TraitorRuleSystem _traitorRule = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnGetProgress); - - SubscribeLocalEvent(OnAssigned); } private void OnGetProgress(EntityUid uid, KeepAliveConditionComponent comp, ref ObjectiveGetProgressEvent args) @@ -35,39 +27,6 @@ public sealed class KeepAliveConditionSystem : EntitySystem args.Progress = GetProgress(target.Value); } - private void OnAssigned(EntityUid uid, RandomTraitorAliveComponent comp, ref ObjectiveAssignedEvent args) - { - // invalid prototype - if (!TryComp(uid, 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(uid, _random.Pick(traitors).Id, target); - } - private float GetProgress(EntityUid target) { if (!TryComp(target, out var mind)) diff --git a/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs b/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs index 3aa4d606fc..45ad68e28b 100644 --- a/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs +++ b/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs @@ -1,12 +1,9 @@ using Content.Server.Objectives.Components; -using Content.Server.Revolutionary.Components; using Content.Server.Shuttles.Systems; using Content.Shared.CCVar; using Content.Shared.Mind; using Content.Shared.Objectives.Components; using Robust.Shared.Configuration; -using Robust.Shared.Random; -using System.Linq; namespace Content.Server.Objectives.Systems; @@ -17,7 +14,6 @@ public sealed class KillPersonConditionSystem : EntitySystem { [Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!; [Dependency] private readonly IConfigurationManager _config = default!; - [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly TargetObjectiveSystem _target = default!; @@ -26,10 +22,6 @@ public sealed class KillPersonConditionSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnGetProgress); - - SubscribeLocalEvent(OnPersonAssigned); - - SubscribeLocalEvent(OnHeadAssigned); } private void OnGetProgress(EntityUid uid, KillPersonConditionComponent comp, ref ObjectiveGetProgressEvent args) @@ -40,74 +32,6 @@ public sealed class KillPersonConditionSystem : EntitySystem args.Progress = GetProgress(target.Value, comp.RequireDead); } - 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; - - var allHumans = _mind.GetAliveHumans(args.MindId); - - // 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.RemoveWhere(x => x.Owner == kill.Target); - } - } - - // no other humans to kill - if (allHumans.Count == 0) - { - args.Cancelled = true; - return; - } - - _target.SetTarget(uid, _random.Pick(allHumans), target); - } - - private void OnHeadAssigned(EntityUid uid, PickRandomHeadComponent comp, ref ObjectiveAssignedEvent args) - { - // invalid 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.GetAliveHumans(args.MindId); - if (allHumans.Count == 0) - { - args.Cancelled = true; - return; - } - - var allHeads = new HashSet>(); - 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 - - _target.SetTarget(uid, _random.Pick(allHeads), target); - } - private float GetProgress(EntityUid target, bool requireDead) { // deleted or gibbed or something, counts as dead diff --git a/Content.Server/Objectives/Systems/PickObjectiveTargetSystem.cs b/Content.Server/Objectives/Systems/PickObjectiveTargetSystem.cs new file mode 100644 index 0000000000..2977e10569 --- /dev/null +++ b/Content.Server/Objectives/Systems/PickObjectiveTargetSystem.cs @@ -0,0 +1,212 @@ +using Content.Server.Objectives.Components; +using Content.Shared.Mind; +using Content.Shared.Objectives.Components; +using Content.Server.GameTicking.Rules; +using Content.Server.Revolutionary.Components; +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 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) + { + // invalid objective prototype + if (!TryComp(ent.Owner, out var target)) + { + args.Cancelled = true; + return; + } + + // target already assigned + if (target.Target != null) + return; + + var allHumans = _mind.GetAliveHumans(args.MindId); + + // 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.RemoveWhere(x => x.Owner == kill.Target); + } + } + + // no other humans to kill + if (allHumans.Count == 0) + { + args.Cancelled = true; + return; + } + + _target.SetTarget(ent.Owner, _random.Pick(allHumans), target); + } + + private void OnRandomHeadAssigned(Entity ent, ref ObjectiveAssignedEvent args) + { + // invalid prototype + if (!TryComp(ent.Owner, out var target)) + { + args.Cancelled = true; + return; + } + + // target already assigned + if (target.Target != null) + return; + + // no other humans to kill + var allHumans = _mind.GetAliveHumans(args.MindId); + if (allHumans.Count == 0) + { + args.Cancelled = true; + return; + } + + var allHeads = new HashSet>(); + foreach (var person in allHumans) + { + if (TryComp(person, out var mind) && mind.OwnedEntity is { } owned && HasComp(owned)) + allHeads.Add(person); + } + + if (allHeads.Count == 0) + allHeads = allHumans; // fallback to non-head target + + _target.SetTarget(ent.Owner, _random.Pick(allHeads), 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); + } +}