Objectives ecs rework (#19967)
Co-authored-by: deltanedas <@deltanedas:kde.org>
This commit is contained in:
22
Content.Server/Objectives/Systems/DieConditionSystem.cs
Normal file
22
Content.Server/Objectives/Systems/DieConditionSystem.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Objectives.Components;
|
||||
|
||||
namespace Content.Server.Objectives.Systems;
|
||||
|
||||
public sealed class DieConditionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DieConditionComponent, ObjectiveGetProgressEvent>(OnGetProgress);
|
||||
}
|
||||
|
||||
private void OnGetProgress(EntityUid uid, DieConditionComponent comp, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
args.Progress = _mind.IsCharacterDeadIc(args.Mind) ? 1f : 0f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Server.Shuttles.Systems;
|
||||
using Content.Shared.Cuffs.Components;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Objectives.Components;
|
||||
|
||||
namespace Content.Server.Objectives.Systems;
|
||||
|
||||
public sealed class EscapeShuttleConditionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<EscapeShuttleConditionComponent, ObjectiveGetProgressEvent>(OnGetProgress);
|
||||
}
|
||||
|
||||
private void OnGetProgress(EntityUid uid, EscapeShuttleConditionComponent comp, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
args.Progress = GetProgress(args.MindId, args.Mind);
|
||||
}
|
||||
|
||||
private float GetProgress(EntityUid mindId, MindComponent mind)
|
||||
{
|
||||
// not escaping alive if you're deleted/dead
|
||||
if (mind.OwnedEntity == null || _mind.IsCharacterDeadIc(mind))
|
||||
return 0f;
|
||||
|
||||
// You're not escaping if you're restrained!
|
||||
if (TryComp<CuffableComponent>(mind.OwnedEntity, out var cuffed) && cuffed.CuffedHandCount > 0)
|
||||
return 0f;
|
||||
|
||||
// Any emergency shuttle counts for this objective, but not pods.
|
||||
return _emergencyShuttle.IsTargetEscaping(mind.OwnedEntity.Value) ? 1f : 0f;
|
||||
}
|
||||
}
|
||||
111
Content.Server/Objectives/Systems/HelpProgressConditionSystem.cs
Normal file
111
Content.Server/Objectives/Systems/HelpProgressConditionSystem.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Handles help progress condition logic and picking random help targets.
|
||||
/// </summary>
|
||||
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<HelpProgressConditionComponent, ObjectiveGetProgressEvent>(OnGetProgress);
|
||||
|
||||
SubscribeLocalEvent<RandomTraitorProgressComponent, ObjectiveAssignedEvent>(OnTraitorAssigned);
|
||||
}
|
||||
|
||||
private void OnGetProgress(EntityUid uid, HelpProgressConditionComponent comp, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
if (!_target.GetTarget(uid, out var target))
|
||||
return;
|
||||
|
||||
args.Progress = GetProgress(target.Value);
|
||||
}
|
||||
|
||||
private void OnTraitorAssigned(EntityUid uid, RandomTraitorProgressComponent comp, ref ObjectiveAssignedEvent args)
|
||||
{
|
||||
// invalid prototype
|
||||
if (!TryComp<TargetObjectiveComponent>(uid, out var target))
|
||||
{
|
||||
args.Cancelled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var traitors = _traitorRule.GetOtherTraitorMindsAliveAndConnected(args.Mind)
|
||||
.Select(pair => pair.Item1)
|
||||
.ToHashSet();
|
||||
var removeList = new List<EntityUid>();
|
||||
|
||||
// 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<ObjectivesComponent>(traitor) or something when objectives are moved out of mind
|
||||
if (!TryComp<MindComponent>(traitor, out var mind))
|
||||
continue;
|
||||
|
||||
foreach (var objective in mind.AllObjectives)
|
||||
{
|
||||
if (HasComp<HelpProgressConditionComponent>(objective))
|
||||
removeList.Add(traitor);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var tot in removeList)
|
||||
{
|
||||
traitors.Remove(tot);
|
||||
}
|
||||
|
||||
// no more helpable traitors
|
||||
if (traitors.Count == 0)
|
||||
{
|
||||
args.Cancelled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_target.SetTarget(uid, _random.Pick(traitors), target);
|
||||
}
|
||||
|
||||
private float GetProgress(EntityUid target)
|
||||
{
|
||||
var total = 0f; // how much progress they have
|
||||
var max = 0f; // how much progress is needed for 100%
|
||||
|
||||
if (TryComp<MindComponent>(target, out var mind))
|
||||
{
|
||||
foreach (var objective in mind.AllObjectives)
|
||||
{
|
||||
// this has the potential to loop forever, anything setting target has to check that there is no HelpProgressCondition.
|
||||
var info = _objectives.GetInfo(objective, target, mind);
|
||||
if (info == null)
|
||||
continue;
|
||||
|
||||
max++; // things can only be up to 100% complete yeah
|
||||
total += info.Value.Progress;
|
||||
}
|
||||
}
|
||||
|
||||
// no objectives that can be helped with...
|
||||
if (max == 0f)
|
||||
return 1f;
|
||||
|
||||
// require 50% completion for this one to be complete
|
||||
var completion = total / max;
|
||||
return completion >= 0.5f ? 1f : completion / 0.5f;
|
||||
}
|
||||
}
|
||||
66
Content.Server/Objectives/Systems/KeepAliveCondition.cs
Normal file
66
Content.Server/Objectives/Systems/KeepAliveCondition.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Handles keep alive condition logic and picking random traitors to keep alive.
|
||||
/// </summary>
|
||||
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<KeepAliveConditionComponent, ObjectiveGetProgressEvent>(OnGetProgress);
|
||||
|
||||
SubscribeLocalEvent<RandomTraitorAliveComponent, ObjectiveAssignedEvent>(OnAssigned);
|
||||
}
|
||||
|
||||
private void OnGetProgress(EntityUid uid, KeepAliveConditionComponent comp, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
if (!_target.GetTarget(uid, out var target))
|
||||
return;
|
||||
|
||||
args.Progress = GetProgress(target.Value);
|
||||
}
|
||||
|
||||
private void OnAssigned(EntityUid uid, RandomTraitorAliveComponent comp, ref ObjectiveAssignedEvent args)
|
||||
{
|
||||
// invalid prototype
|
||||
if (!TryComp<TargetObjectiveComponent>(uid, out var target))
|
||||
{
|
||||
args.Cancelled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var traitors = Enumerable.ToList<(EntityUid Id, MindComponent Mind)>(_traitorRule.GetOtherTraitorMindsAliveAndConnected(args.Mind));
|
||||
|
||||
// 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<MindComponent>(target, out var mind))
|
||||
return 0f;
|
||||
|
||||
return _mind.IsCharacterDeadIc(mind) ? 0f : 1f;
|
||||
}
|
||||
}
|
||||
131
Content.Server/Objectives/Systems/KillPersonConditionSystem.cs
Normal file
131
Content.Server/Objectives/Systems/KillPersonConditionSystem.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Server.Shuttles.Systems;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Roles.Jobs;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Objectives.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles kill person condition logic and picking random kill targets.
|
||||
/// </summary>
|
||||
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 SharedJobSystem _job = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly TargetObjectiveSystem _target = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<KillPersonConditionComponent, ObjectiveGetProgressEvent>(OnGetProgress);
|
||||
|
||||
SubscribeLocalEvent<PickRandomPersonComponent, ObjectiveAssignedEvent>(OnPersonAssigned);
|
||||
|
||||
SubscribeLocalEvent<PickRandomHeadComponent, ObjectiveAssignedEvent>(OnHeadAssigned);
|
||||
}
|
||||
|
||||
private void OnGetProgress(EntityUid uid, KillPersonConditionComponent comp, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
if (!_target.GetTarget(uid, out var target))
|
||||
return;
|
||||
|
||||
args.Progress = GetProgress(target.Value, comp.RequireDead);
|
||||
}
|
||||
|
||||
private void OnPersonAssigned(EntityUid uid, PickRandomPersonComponent comp, ref ObjectiveAssignedEvent args)
|
||||
{
|
||||
// invalid objective prototype
|
||||
if (!TryComp<TargetObjectiveComponent>(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);
|
||||
}
|
||||
|
||||
private void OnHeadAssigned(EntityUid uid, PickRandomHeadComponent comp, ref ObjectiveAssignedEvent args)
|
||||
{
|
||||
// invalid prototype
|
||||
if (!TryComp<TargetObjectiveComponent>(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;
|
||||
}
|
||||
|
||||
var allHeads = new List<EntityUid>();
|
||||
foreach (var mind in allHumans)
|
||||
{
|
||||
// RequireAdminNotify used as a cheap way to check for command department
|
||||
if (_job.MindTryGetJob(mind, out _, out var prototype) && prototype.RequireAdminNotify)
|
||||
allHeads.Add(mind);
|
||||
}
|
||||
|
||||
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
|
||||
if (!TryComp<MindComponent>(target, out var mind) || mind.OwnedEntity == null)
|
||||
return 1f;
|
||||
|
||||
// dead is success
|
||||
if (_mind.IsCharacterDeadIc(mind))
|
||||
return 1f;
|
||||
|
||||
// if the target has to be dead dead then don't check evac stuff
|
||||
if (requireDead)
|
||||
return 0f;
|
||||
|
||||
// if evac is disabled then they really do have to be dead
|
||||
if (!_config.GetCVar(CCVars.EmergencyShuttleEnabled))
|
||||
return 0f;
|
||||
|
||||
// target is escaping so you fail
|
||||
if (_emergencyShuttle.IsTargetEscaping(mind.OwnedEntity.Value))
|
||||
return 0f;
|
||||
|
||||
// evac has left without the target, greentext since the target is afk in space with a full oxygen tank and coordinates off.
|
||||
if (_emergencyShuttle.ShuttlesLeft)
|
||||
return 1f;
|
||||
|
||||
// if evac is still here and target hasn't boarded, show 50% to give you an indicator that you are doing good
|
||||
return _emergencyShuttle.EmergencyShuttleArrived ? 0.5f : 0f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Content.Server.GameTicking.Rules;
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Shared.Objectives.Components;
|
||||
|
||||
namespace Content.Server.Objectives.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles requiring multiple traitors being alive for the objective to be given.
|
||||
/// </summary>
|
||||
public sealed class MultipleTraitorsRequirementSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly TraitorRuleSystem _traitorRule = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MultipleTraitorsRequirementComponent, RequirementCheckEvent>(OnCheck);
|
||||
}
|
||||
|
||||
private void OnCheck(EntityUid uid, MultipleTraitorsRequirementComponent comp, ref RequirementCheckEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
if (_traitorRule.GetOtherTraitorMindsAliveAndConnected(args.Mind).Count < comp.Traitors)
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
106
Content.Server/Objectives/Systems/NinjaConditionsSystem.cs
Normal file
106
Content.Server/Objectives/Systems/NinjaConditionsSystem.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Server.Warps;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Objectives.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the objective conditions that hard depend on ninja.
|
||||
/// Survive is handled by <see cref="SurviveConditionSystem"/> since it works without being a ninja.
|
||||
/// </summary>
|
||||
public sealed class NinjaConditionsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||
[Dependency] private readonly NumberObjectiveSystem _number = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<DoorjackConditionComponent, ObjectiveGetProgressEvent>(OnDoorjackGetProgress);
|
||||
|
||||
SubscribeLocalEvent<SpiderChargeConditionComponent, ObjectiveAfterAssignEvent>(OnSpiderChargeAfterAssign);
|
||||
SubscribeLocalEvent<SpiderChargeConditionComponent, ObjectiveGetProgressEvent>(OnSpiderChargeGetProgress);
|
||||
|
||||
SubscribeLocalEvent<StealResearchConditionComponent, ObjectiveGetProgressEvent>(OnStealResearchGetProgress);
|
||||
|
||||
SubscribeLocalEvent<TerrorConditionComponent, ObjectiveGetProgressEvent>(OnTerrorGetProgress);
|
||||
}
|
||||
|
||||
// doorjack
|
||||
|
||||
private void OnDoorjackGetProgress(EntityUid uid, DoorjackConditionComponent comp, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
args.Progress = DoorjackProgress(args.MindId, _number.GetTarget(uid));
|
||||
}
|
||||
|
||||
private float DoorjackProgress(EntityUid mindId, int target)
|
||||
{
|
||||
// prevent divide-by-zero
|
||||
if (target == 0)
|
||||
return 1f;
|
||||
|
||||
if (!TryComp<NinjaRoleComponent>(mindId, out var role))
|
||||
return 0f;
|
||||
|
||||
if (role.DoorsJacked >= target)
|
||||
return 1f;
|
||||
|
||||
return (float) role.DoorsJacked / (float) target;
|
||||
}
|
||||
|
||||
// spider charge
|
||||
|
||||
private void OnSpiderChargeAfterAssign(EntityUid uid, SpiderChargeConditionComponent comp, ref ObjectiveAfterAssignEvent args)
|
||||
{
|
||||
_metaData.SetEntityName(uid, SpiderChargeTitle(args.MindId), args.Meta);
|
||||
}
|
||||
|
||||
private void OnSpiderChargeGetProgress(EntityUid uid, SpiderChargeConditionComponent comp, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
args.Progress = TryComp<NinjaRoleComponent>(args.MindId, out var role) && role.SpiderChargeDetonated ? 1f : 0f;
|
||||
}
|
||||
|
||||
private string SpiderChargeTitle(EntityUid mindId)
|
||||
{
|
||||
if (!TryComp<NinjaRoleComponent>(mindId, out var role) ||
|
||||
role.SpiderChargeTarget == null ||
|
||||
!TryComp<WarpPointComponent>(role.SpiderChargeTarget, out var warp) ||
|
||||
warp.Location == null)
|
||||
{
|
||||
// this should never really happen but eh
|
||||
return Loc.GetString("objective-condition-spider-charge-title-no-target");
|
||||
}
|
||||
|
||||
return Loc.GetString("objective-condition-spider-charge-title", ("location", warp.Location));
|
||||
}
|
||||
|
||||
// steal research
|
||||
|
||||
private void OnStealResearchGetProgress(EntityUid uid, StealResearchConditionComponent comp, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
args.Progress = StealResearchProgress(args.MindId, _number.GetTarget(uid));
|
||||
}
|
||||
|
||||
private float StealResearchProgress(EntityUid mindId, int target)
|
||||
{
|
||||
// prevent divide-by-zero
|
||||
if (target == 0)
|
||||
return 1f;
|
||||
|
||||
if (!TryComp<NinjaRoleComponent>(mindId, out var role))
|
||||
return 0f;
|
||||
|
||||
if (role.DownloadedNodes.Count >= target)
|
||||
return 1f;
|
||||
|
||||
return (float) role.DownloadedNodes.Count / (float) target;
|
||||
}
|
||||
|
||||
// terror
|
||||
|
||||
private void OnTerrorGetProgress(EntityUid uid, TerrorConditionComponent comp, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
args.Progress = TryComp<NinjaRoleComponent>(args.MindId, out var role) && role.CalledInThreat ? 1f : 0f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Roles.Jobs;
|
||||
|
||||
namespace Content.Server.Objectives.Systems;
|
||||
|
||||
public sealed class NotCommandRequirementSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedJobSystem _job = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NotCommandRequirementComponent, RequirementCheckEvent>(OnCheck);
|
||||
}
|
||||
|
||||
private void OnCheck(EntityUid uid, NotCommandRequirementComponent comp, ref RequirementCheckEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
// cheap equivalent to checking that job department is command, since all command members require admin notification when leaving
|
||||
if (_job.MindTryGetJob(args.MindId, out _, out var prototype) && prototype.RequireAdminNotify)
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
31
Content.Server/Objectives/Systems/NotJobRequirementSystem.cs
Normal file
31
Content.Server/Objectives/Systems/NotJobRequirementSystem.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Roles.Jobs;
|
||||
|
||||
namespace Content.Server.Objectives.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles checking the job blacklist for this objective.
|
||||
/// </summary>
|
||||
public sealed class NotJobRequirementSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NotJobRequirementComponent, RequirementCheckEvent>(OnCheck);
|
||||
}
|
||||
|
||||
private void OnCheck(EntityUid uid, NotJobRequirementComponent comp, ref RequirementCheckEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
// if player has no job then don't care
|
||||
if (!TryComp<JobComponent>(args.MindId, out var job))
|
||||
return;
|
||||
|
||||
if (job.PrototypeId == comp.Job)
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
48
Content.Server/Objectives/Systems/NumberObjectiveSystem.cs
Normal file
48
Content.Server/Objectives/Systems/NumberObjectiveSystem.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Objectives.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Provides API for other components, handles picking the count and setting the title and description.
|
||||
/// </summary>
|
||||
public sealed class NumberObjectiveSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NumberObjectiveComponent, ObjectiveAssignedEvent>(OnAssigned);
|
||||
SubscribeLocalEvent<NumberObjectiveComponent, ObjectiveAfterAssignEvent>(OnAfterAssign);
|
||||
}
|
||||
|
||||
private void OnAssigned(EntityUid uid, NumberObjectiveComponent comp, ref ObjectiveAssignedEvent args)
|
||||
{
|
||||
comp.Target = _random.Next(comp.Min, comp.Max);
|
||||
}
|
||||
|
||||
private void OnAfterAssign(EntityUid uid, NumberObjectiveComponent comp, ref ObjectiveAfterAssignEvent args)
|
||||
{
|
||||
if (comp.Title != null)
|
||||
_metaData.SetEntityName(uid, Loc.GetString(comp.Title, ("count", comp.Target)), args.Meta);
|
||||
|
||||
if (comp.Description != null)
|
||||
_metaData.SetEntityDescription(uid, Loc.GetString(comp.Description, ("count", comp.Target)), args.Meta);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the objective's target count.
|
||||
/// </summary>
|
||||
public int GetTarget(EntityUid uid, NumberObjectiveComponent? comp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp))
|
||||
return 0;
|
||||
|
||||
return comp.Target;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Shared.Objectives.Components;
|
||||
|
||||
namespace Content.Server.Objectives.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles applying the objective component blacklist to the objective entity.
|
||||
/// </summary>
|
||||
public sealed class ObjectiveBlacklistRequirementSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ObjectiveBlacklistRequirementComponent, RequirementCheckEvent>(OnCheck);
|
||||
}
|
||||
|
||||
private void OnCheck(EntityUid uid, ObjectiveBlacklistRequirementComponent comp, ref RequirementCheckEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
if (comp.Blacklist.IsValid(uid, EntityManager))
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
28
Content.Server/Objectives/Systems/RoleRequirementSystem.cs
Normal file
28
Content.Server/Objectives/Systems/RoleRequirementSystem.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Shared.Objectives.Components;
|
||||
|
||||
namespace Content.Server.Objectives.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles role requirement for objectives that require a certain (probably antagonist) role(s).
|
||||
/// </summary>
|
||||
public sealed class RoleRequirementSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RoleRequirementComponent, RequirementCheckEvent>(OnCheck);
|
||||
}
|
||||
|
||||
private void OnCheck(EntityUid uid, RoleRequirementComponent comp, ref RequirementCheckEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
// this whitelist trick only works because roles are components on the mind and not entities
|
||||
// if that gets reworked then this will need changing
|
||||
if (!comp.Roles.IsValid(args.MindId, EntityManager))
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Server.Roles;
|
||||
using Content.Shared.Objectives.Components;
|
||||
|
||||
namespace Content.Server.Objectives.Systems;
|
||||
|
||||
public sealed class SpiderChargeTargetRequirementSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SpiderChargeTargetRequirementComponent, RequirementCheckEvent>(OnCheck);
|
||||
}
|
||||
|
||||
private void OnCheck(EntityUid uid, SpiderChargeTargetRequirementComponent comp, ref RequirementCheckEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
if (!TryComp<NinjaRoleComponent>(args.MindId, out var role) || role.SpiderChargeTarget == null)
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
93
Content.Server/Objectives/Systems/StealConditionSystem.cs
Normal file
93
Content.Server/Objectives/Systems/StealConditionSystem.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Objectives.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Objectives.Systems;
|
||||
|
||||
public sealed class StealConditionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
|
||||
|
||||
private EntityQuery<ContainerManagerComponent> containerQuery;
|
||||
private EntityQuery<MetaDataComponent> metaQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
containerQuery = GetEntityQuery<ContainerManagerComponent>();
|
||||
metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
|
||||
SubscribeLocalEvent<StealConditionComponent, ObjectiveAssignedEvent>(OnAssigned);
|
||||
SubscribeLocalEvent<StealConditionComponent, ObjectiveAfterAssignEvent>(OnAfterAssign);
|
||||
SubscribeLocalEvent<StealConditionComponent, ObjectiveGetProgressEvent>(OnGetProgress);
|
||||
}
|
||||
|
||||
private void OnAssigned(EntityUid uid, StealConditionComponent comp, ref ObjectiveAssignedEvent args)
|
||||
{
|
||||
// cancel if the item to steal doesn't exist
|
||||
args.Cancelled |= !_proto.HasIndex<EntityPrototype>(comp.Prototype);
|
||||
}
|
||||
|
||||
private void OnAfterAssign(EntityUid uid, StealConditionComponent comp, ref ObjectiveAfterAssignEvent args)
|
||||
{
|
||||
var proto = _proto.Index<EntityPrototype>(comp.Prototype);
|
||||
var title = comp.OwnerText == null
|
||||
? Loc.GetString("objective-condition-steal-title-no-owner", ("itemName", proto.Name))
|
||||
: Loc.GetString("objective-condition-steal-title", ("owner", Loc.GetString(comp.OwnerText)), ("itemName", proto.Name));
|
||||
var description = Loc.GetString("objective-condition-steal-description", ("itemName", proto.Name));
|
||||
|
||||
_metaData.SetEntityName(uid, title, args.Meta);
|
||||
_metaData.SetEntityDescription(uid, description, args.Meta);
|
||||
_objectives.SetIcon(uid, new SpriteSpecifier.EntityPrototype(comp.Prototype), args.Objective);
|
||||
}
|
||||
|
||||
private void OnGetProgress(EntityUid uid, StealConditionComponent comp, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
args.Progress = GetProgress(args.Mind, comp.Prototype);
|
||||
}
|
||||
|
||||
private float GetProgress(MindComponent mind, string prototype)
|
||||
{
|
||||
// TODO make this a container system function
|
||||
// or: just iterate through transform children, instead of containers?
|
||||
|
||||
if (!metaQuery.TryGetComponent(mind.OwnedEntity, out var meta))
|
||||
return 0;
|
||||
|
||||
// who added this check bruh
|
||||
if (meta.EntityPrototype?.ID == prototype)
|
||||
return 1;
|
||||
|
||||
if (!containerQuery.TryGetComponent(mind.OwnedEntity, out var currentManager))
|
||||
return 0;
|
||||
|
||||
// recursively check each container for the item
|
||||
// checks inventory, bag, implants, etc.
|
||||
var stack = new Stack<ContainerManagerComponent>();
|
||||
do
|
||||
{
|
||||
foreach (var container in currentManager.Containers.Values)
|
||||
{
|
||||
foreach (var entity in container.ContainedEntities)
|
||||
{
|
||||
// check if this is the item
|
||||
if (metaQuery.GetComponent(entity).EntityPrototype?.ID == prototype)
|
||||
return 1;
|
||||
|
||||
// if it is a container check its contents
|
||||
if (containerQuery.TryGetComponent(entity, out var containerManager))
|
||||
stack.Push(containerManager);
|
||||
}
|
||||
}
|
||||
} while (stack.TryPop(out currentManager));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
25
Content.Server/Objectives/Systems/SurviveConditionSystem.cs
Normal file
25
Content.Server/Objectives/Systems/SurviveConditionSystem.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Mind;
|
||||
|
||||
namespace Content.Server.Objectives.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles progress for the survive objective condition.
|
||||
/// </summary>
|
||||
public sealed class SurviveConditionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SurviveConditionComponent, ObjectiveGetProgressEvent>(OnGetProgress);
|
||||
}
|
||||
|
||||
private void OnGetProgress(EntityUid uid, SurviveConditionComponent comp, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
args.Progress = _mind.IsCharacterDeadIc(args.Mind) ? 0f : 1f;
|
||||
}
|
||||
}
|
||||
68
Content.Server/Objectives/Systems/TargetObjectiveSystem.cs
Normal file
68
Content.Server/Objectives/Systems/TargetObjectiveSystem.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Roles.Jobs;
|
||||
using Robust.Shared.GameObjects;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Content.Server.Objectives.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Provides API for other components and handles setting the title.
|
||||
/// </summary>
|
||||
public sealed class TargetObjectiveSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||
[Dependency] private readonly SharedJobSystem _job = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TargetObjectiveComponent, ObjectiveAfterAssignEvent>(OnAfterAssign);
|
||||
}
|
||||
|
||||
private void OnAfterAssign(EntityUid uid, TargetObjectiveComponent comp, ref ObjectiveAfterAssignEvent args)
|
||||
{
|
||||
if (!GetTarget(uid, out var target, comp))
|
||||
return;
|
||||
|
||||
_metaData.SetEntityName(uid, GetTitle(target.Value, comp.Title), args.Meta);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Target field for the title and other components to use.
|
||||
/// </summary>
|
||||
public void SetTarget(EntityUid uid, EntityUid target, TargetObjectiveComponent? comp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp))
|
||||
return;
|
||||
|
||||
comp.Target = target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target from the component.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If it is null then the prototype is invalid, just return.
|
||||
/// </remarks>
|
||||
public bool GetTarget(EntityUid uid, [NotNullWhen(true)] out EntityUid? target, TargetObjectiveComponent? comp = null)
|
||||
{
|
||||
target = Resolve(uid, ref comp) ? comp.Target : null;
|
||||
return target != null;
|
||||
}
|
||||
|
||||
private string GetTitle(EntityUid target, string title)
|
||||
{
|
||||
var targetName = "Unknown";
|
||||
if (TryComp<MindComponent>(target, out var mind) && mind.CharacterName != null)
|
||||
{
|
||||
targetName = mind.CharacterName;
|
||||
}
|
||||
|
||||
var jobName = _job.MindTryGetJobName(target);
|
||||
return Loc.GetString(title, ("targetName", targetName), ("job", jobName));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user