+++ /dev/null
-using Content.Server.GameTicking.Rules;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
-
-namespace Content.Server.GameTicking.Rules.Components;
-
-/// <summary>
-/// Gamerule for simple antagonists that have fixed objectives.
-/// </summary>
-[RegisterComponent, Access(typeof(GenericAntagRuleSystem))]
-public sealed partial class GenericAntagRuleComponent : Component
-{
- /// <summary>
- /// All antag minds that are using this rule.
- /// </summary>
- [DataField]
- public List<EntityUid> Minds = new();
-
- /// <summary>
- /// Locale id for the name of the antag used by the roundend summary.
- /// </summary>
- [DataField(required: true), ViewVariables(VVAccess.ReadWrite)]
- public string AgentName = string.Empty;
-
- /// <summary>
- /// List of objective entity prototypes to add to the antag when a mind is added.
- /// </summary>
- [DataField(required: true)]
- public List<EntProtoId> Objectives = new();
-}
+++ /dev/null
-using Content.Server.GameTicking.Rules.Components;
-using Content.Server.Objectives;
-using Content.Shared.Mind;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-
-namespace Content.Server.GameTicking.Rules;
-
-/// <summary>
-/// Handles round end text for simple antags.
-/// Adding objectives is handled in its own system.
-/// </summary>
-public sealed class GenericAntagRuleSystem : GameRuleSystem<GenericAntagRuleComponent>
-{
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent<GenericAntagRuleComponent, ObjectivesTextGetInfoEvent>(OnObjectivesTextGetInfo);
- }
-
- /// <summary>
- /// Start a simple antag's game rule.
- /// If it is invalid the rule is deleted and null is returned.
- /// </summary>
- public bool StartRule(string rule, EntityUid mindId, [NotNullWhen(true)] out EntityUid? ruleId, [NotNullWhen(true)] out GenericAntagRuleComponent? comp)
- {
- ruleId = GameTicker.AddGameRule(rule);
- if (!TryComp<GenericAntagRuleComponent>(ruleId, out comp))
- {
- Log.Error($"Simple antag rule prototype {rule} is invalid, deleting it.");
- Del(ruleId);
- ruleId = null;
- return false;
- }
-
- if (!GameTicker.StartGameRule(ruleId.Value))
- {
- Log.Error($"Simple antag rule prototype {rule} failed to start, deleting it.");
- Del(ruleId);
- ruleId = null;
- comp = null;
- return false;
- }
-
- comp.Minds.Add(mindId);
- return true;
- }
-
- private void OnObjectivesTextGetInfo(EntityUid uid, GenericAntagRuleComponent comp, ref ObjectivesTextGetInfoEvent args)
- {
- // just temporary until this is deleted
- args.Minds = comp.Minds.Select(mindId => (mindId, Comp<MindComponent>(mindId).CharacterName ?? "?")).ToList();
- args.AgentName = Loc.GetString(comp.AgentName);
- }
-}
+++ /dev/null
-using Content.Server.GameTicking.Rules.Components;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-
-namespace Content.Server.GenericAntag;
-
-/// <summary>
-/// Added to a mob to make it a generic antagonist where all its objectives are fixed.
-/// This is unlike say traitor where it gets objectives picked randomly using difficulty.
-/// </summary>
-/// <remarks>
-/// A GenericAntag is not necessarily an antagonist, that depends on the roles you do or do not add after.
-/// </remarks>
-[RegisterComponent, Access(typeof(GenericAntagSystem))]
-public sealed partial class GenericAntagComponent : Component
-{
- /// <summary>
- /// Gamerule to start when a mind is added.
- /// This must have <see cref="GenericAntagRuleComponent"/> or it will not work.
- /// </summary>
- [DataField(required: true), ViewVariables(VVAccess.ReadWrite)]
- public EntProtoId Rule = string.Empty;
-
- /// <summary>
- /// The rule that's been spawned.
- /// Used to prevent spawning multiple rules.
- /// </summary>
- [DataField, ViewVariables(VVAccess.ReadWrite)]
- public EntityUid? RuleEntity;
-}
+++ /dev/null
-using Content.Server.GameTicking.Rules;
-using Content.Shared.Mind;
-using Content.Shared.Mind.Components;
-
-namespace Content.Server.GenericAntag;
-
-/// <summary>
-/// Handles adding objectives to <see cref="GenericAntagComponent"/>s.
-/// Roundend summary is handled by <see cref="GenericAntagRuleSystem"/>.
-/// </summary>
-public sealed class GenericAntagSystem : EntitySystem
-{
- [Dependency] private readonly SharedMindSystem _mind = default!;
- [Dependency] private readonly GenericAntagRuleSystem _genericAntagRule = default!;
-
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent<GenericAntagComponent, MindAddedMessage>(OnMindAdded);
- }
-
- private void OnMindAdded(EntityUid uid, GenericAntagComponent comp, MindAddedMessage args)
- {
- if (!TryComp<MindContainerComponent>(uid, out var mindContainer) || mindContainer.Mind == null)
- return;
-
- var mindId = mindContainer.Mind.Value;
- MakeAntag(uid, mindId, comp);
- }
-
- /// <summary>
- /// Turns a player into this antagonist.
- /// Does the same thing that having a mind added does, use for antag ctrl.
- /// </summary>
- public void MakeAntag(EntityUid uid, EntityUid mindId, GenericAntagComponent? comp = null, MindComponent? mind = null)
- {
- if (!Resolve(uid, ref comp) || !Resolve(mindId, ref mind))
- return;
-
- // only add the rule once
- if (comp.RuleEntity != null)
- return;
-
- // start the rule
- if (!_genericAntagRule.StartRule(comp.Rule, mindId, out comp.RuleEntity, out var rule))
- return;
-
- // let other systems know the antag was created so they can add briefing, roles, etc.
- // its important that this is before objectives are added since they may depend on roles added here
- var ev = new GenericAntagCreatedEvent(mindId, mind);
- RaiseLocalEvent(uid, ref ev);
-
- // add the objectives from the rule
- foreach (var id in rule.Objectives)
- {
- _mind.TryAddObjective(mindId, mind, id);
- }
- }
-}
-
-/// <summary>
-/// Event raised on a player's entity after its simple antag rule is started.
-/// Use this to add a briefing, roles, etc.
-/// </summary>
-[ByRefEvent]
-public record struct GenericAntagCreatedEvent(EntityUid MindId, MindComponent Mind);