]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Roundstart antag role restrictions revival (#20108)
authordeltanedas <39013340+deltanedas@users.noreply.github.com>
Wed, 20 Sep 2023 07:54:53 +0000 (08:54 +0100)
committerGitHub <noreply@github.com>
Wed, 20 Sep 2023 07:54:53 +0000 (17:54 +1000)
Co-authored-by: Ray <vigersray@gmail.com>
Co-authored-by: deltanedas <@deltanedas:kde.org>
14 files changed:
Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs
Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs
Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRoleEntryButtons.xaml
Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRoleRulesWindow.xaml
Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesEntry.xaml.cs
Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesEui.cs
Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesWindow.xaml
Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesWindow.xaml.cs
Content.Server/Ghost/Roles/Components/GhostRoleComponent.cs
Content.Server/Ghost/Roles/GhostRoleSystem.cs
Content.Shared/Ghost/Roles/GhostRolesEuiMessages.cs
Content.Shared/Roles/AntagPrototype.cs
Content.Shared/Roles/JobRequirements.cs
Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl

index 9d5801b925e87db09423b4c1932944c431e621da..c67c759dfdd895bf52f4af3d38f04bc35171e5e4 100644 (file)
@@ -97,21 +97,29 @@ public sealed class JobRequirementsManager
         }
 
         var player = _playerManager.LocalPlayer?.Session;
-
         if (player == null)
             return true;
 
-        var reasonBuilder = new StringBuilder();
+        return CheckRoleTime(job.Requirements, out reason);
+    }
+
+    public bool CheckRoleTime(HashSet<JobRequirement>? requirements, [NotNullWhen(false)] out FormattedMessage? reason)
+    {
+        reason = null;
+
+        if (requirements == null)
+            return true;
 
-        foreach (var requirement in job.Requirements)
+        var reasons = new List<string>();
+        foreach (var requirement in requirements)
         {
             if (JobRequirements.TryRequirementMet(requirement, _roles, out var jobReason, _entManager, _prototypes))
                 continue;
 
-            reasonBuilder.AppendLine(jobReason.ToMarkup());
+            reasons.Add(jobReason.ToMarkup());
         }
 
-        reason = reasonBuilder.Length == 0 ? null : FormattedMessage.FromMarkup(reasonBuilder.ToString().Trim());
+        reason = reasons.Count == 0 ? null : FormattedMessage.FromMarkup(string.Join('\n', reasons));
         return reason == null;
     }
 }
index 59e08d344f8d9c20182ce8fac6151f92d81f522c..c9a64eb0973dd6c74d169677b04b34218ddc2458 100644 (file)
@@ -393,13 +393,16 @@ namespace Content.Client.Preferences.UI
             foreach (var antag in prototypeManager.EnumeratePrototypes<AntagPrototype>().OrderBy(a => Loc.GetString(a.Name)))
             {
                 if (!antag.SetPreference)
-                {
                     continue;
-                }
 
                 var selector = new AntagPreferenceSelector(antag);
                 _antagList.AddChild(selector);
                 _antagPreferences.Add(selector);
+                if (selector.Disabled)
+                {
+                    Profile = Profile?.WithAntagPreference(antag.ID, false);
+                    IsDirty = true;
+                }
 
                 selector.PreferenceChanged += preference =>
                 {
@@ -587,19 +590,15 @@ namespace Content.Client.Preferences.UI
                         foreach (var jobSelector in _jobPriorities)
                         {
                             // Sync other selectors with the same job in case of multiple department jobs
-                            if (jobSelector.Job == selector.Job)
+                            if (jobSelector.Proto == selector.Proto)
                             {
                                 jobSelector.Priority = priority;
                             }
-
-                            // Lower any other high priorities to medium.
-                            if (priority == JobPriority.High)
+                            else if (priority == JobPriority.High && jobSelector.Priority == JobPriority.High)
                             {
-                                if (jobSelector.Job != selector.Job && jobSelector.Priority == JobPriority.High)
-                                {
-                                    jobSelector.Priority = JobPriority.Medium;
-                                    Profile = Profile?.WithJobPriority(jobSelector.Job.ID, JobPriority.Medium);
-                                }
+                                // Lower any other high priorities to medium.
+                                jobSelector.Priority = JobPriority.Medium;
+                                Profile = Profile?.WithJobPriority(jobSelector.Proto.ID, JobPriority.Medium);
                             }
                         }
                     };
@@ -1131,7 +1130,7 @@ namespace Content.Client.Preferences.UI
         {
             foreach (var prioritySelector in _jobPriorities)
             {
-                var jobId = prioritySelector.Job.ID;
+                var jobId = prioritySelector.Proto.ID;
 
                 var priority = Profile?.JobPriorities.GetValueOrDefault(jobId, JobPriority.Never) ?? JobPriority.Never;
 
@@ -1139,55 +1138,29 @@ namespace Content.Client.Preferences.UI
             }
         }
 
-        private sealed class JobPrioritySelector : Control
+        private abstract class RequirementsSelector<T> : Control
         {
-            public JobPrototype Job { get; }
-            private readonly RadioOptions<int> _optionButton;
-
-            public JobPriority Priority
-            {
-                get => (JobPriority) _optionButton.SelectedValue;
-                set => _optionButton.SelectByValue((int) value);
-            }
-
-            public event Action<JobPriority>? PriorityChanged;
+            public T Proto { get; }
+            public bool Disabled => _lockStripe.Visible;
 
+            protected readonly RadioOptions<int> Options;
             private StripeBack _lockStripe;
             private Label _requirementsLabel;
-            private Label _jobTitle;
 
-            public JobPrioritySelector(JobPrototype job, IPrototypeManager prototypeManager)
+            protected RequirementsSelector(T proto)
             {
-                Job = job;
+                Proto = proto;
 
-                _optionButton = new RadioOptions<int>(RadioOptionsLayout.Horizontal)
+                Options = new RadioOptions<int>(RadioOptionsLayout.Horizontal)
                 {
                     FirstButtonStyle = StyleBase.ButtonOpenRight,
                     ButtonStyle = StyleBase.ButtonOpenBoth,
                     LastButtonStyle = StyleBase.ButtonOpenLeft
                 };
                 //Override default radio option button width
-                _optionButton.GenerateItem = GenerateButton;
-                // Text, Value
-                _optionButton.AddItem(Loc.GetString("humanoid-profile-editor-job-priority-high-button"), (int) JobPriority.High);
-                _optionButton.AddItem(Loc.GetString("humanoid-profile-editor-job-priority-medium-button"), (int) JobPriority.Medium);
-                _optionButton.AddItem(Loc.GetString("humanoid-profile-editor-job-priority-low-button"), (int) JobPriority.Low);
-                _optionButton.AddItem(Loc.GetString("humanoid-profile-editor-job-priority-never-button"), (int) JobPriority.Never);
-
-                _optionButton.OnItemSelected += args =>
-                {
-                    _optionButton.Select(args.Id);
-                    PriorityChanged?.Invoke(Priority);
-                };
-
-                var icon = new TextureRect
-                {
-                    TextureScale = new Vector2(2, 2),
-                    Stretch = TextureRect.StretchMode.KeepCentered
-                };
+                Options.GenerateItem = GenerateButton;
 
-                var jobIcon = prototypeManager.Index<StatusIconPrototype>(job.Icon);
-                icon.Texture = jobIcon.Icon.Frame0();
+                Options.OnItemSelected += args => Options.Select(args.Id);
 
                 _requirementsLabel = new Label()
                 {
@@ -1208,30 +1181,40 @@ namespace Content.Client.Preferences.UI
                     }
                 };
 
-                _jobTitle = new Label()
-                {
-                    Margin = new Thickness(5f,0,5f,0),
-                    Text = job.LocalizedName,
-                    MinSize = new Vector2(200, 0),
-                    MouseFilter = MouseFilterMode.Stop
-                };
+                // Setup must be called after
+            }
 
-                if (job.LocalizedDescription != null)
+            /// <summary>
+            /// Actually adds the controls, must be called in the inheriting class' constructor.
+            /// </summary>
+            protected void Setup((string, int)[] items, string title, int titleSize, string? description, TextureRect? icon = null)
+            {
+                foreach (var (text, value) in items)
                 {
-                    _jobTitle.ToolTip = job.LocalizedDescription;
+                    Options.AddItem(Loc.GetString(text), value);
                 }
 
-                AddChild(new BoxContainer
+                var titleLabel = new Label()
+                {
+                    Margin = new Thickness(5f, 0, 5f, 0),
+                    Text = title,
+                    MinSize = new Vector2(titleSize, 0),
+                    MouseFilter = MouseFilterMode.Stop,
+                    ToolTip = description
+                };
+
+                var container = new BoxContainer
                 {
                     Orientation = LayoutOrientation.Horizontal,
-                    Children =
-                    {
-                        icon,
-                        _jobTitle,
-                        _optionButton,
-                        _lockStripe,
-                    }
-                });
+                };
+
+                if (icon != null)
+                    container.AddChild(icon);
+                container.AddChild(titleLabel);
+                container.AddChild(Options);
+                container.AddChild(_lockStripe);
+
+                AddChild(container);
             }
 
             public void LockRequirements(FormattedMessage requirements)
@@ -1240,25 +1223,58 @@ namespace Content.Client.Preferences.UI
                 tooltip.SetMessage(requirements);
                 _lockStripe.TooltipSupplier = _ => tooltip;
                 _lockStripe.Visible = true;
-                _optionButton.Visible = false;
+                Options.Visible = false;
             }
 
             // TODO: Subscribe to roletimers event. I am too lazy to do this RN But I doubt most people will notice fn
             public void UnlockRequirements()
             {
-                _requirementsLabel.Visible = false;
                 _lockStripe.Visible = false;
-                _optionButton.Visible = true;
+                Options.Visible = true;
             }
 
             private Button GenerateButton(string text, int value)
             {
-                var btn = new Button
+                return new Button
                 {
                     Text = text,
                     MinWidth = 90
                 };
-                return btn;
+            }
+        }
+
+        private sealed class JobPrioritySelector : RequirementsSelector<JobPrototype>
+        {
+            public JobPriority Priority
+            {
+                get => (JobPriority) Options.SelectedValue;
+                set => Options.SelectByValue((int) value);
+            }
+
+            public event Action<JobPriority>? PriorityChanged;
+
+            public JobPrioritySelector(JobPrototype proto, IPrototypeManager protoMan)
+                : base(proto)
+            {
+                Options.OnItemSelected += args => PriorityChanged?.Invoke(Priority);
+
+                var items = new[]
+                {
+                    ("humanoid-profile-editor-job-priority-high-button", (int) JobPriority.High),
+                    ("humanoid-profile-editor-job-priority-medium-button", (int) JobPriority.Medium),
+                    ("humanoid-profile-editor-job-priority-low-button", (int) JobPriority.Low),
+                    ("humanoid-profile-editor-job-priority-never-button", (int) JobPriority.Never),
+                };
+
+                var icon = new TextureRect
+                {
+                    TextureScale = new Vector2(2, 2),
+                    Stretch = TextureRect.StretchMode.KeepCentered
+                };
+                var jobIcon = protoMan.Index<StatusIconPrototype>(proto.Icon);
+                icon.Texture = jobIcon.Icon.Frame0();
+
+                Setup(items, proto.LocalizedName, 200, proto.LocalizedDescription, icon);
             }
         }
 
@@ -1266,9 +1282,8 @@ namespace Content.Client.Preferences.UI
         {
             foreach (var preferenceSelector in _antagPreferences)
             {
-                var antagId = preferenceSelector.Antag.ID;
+                var antagId = preferenceSelector.Proto.ID;
                 var preference = Profile?.AntagPreferences.Contains(antagId) ?? false;
-
                 preferenceSelector.Preference = preference;
             }
         }
@@ -1284,44 +1299,38 @@ namespace Content.Client.Preferences.UI
             }
         }
 
-        private sealed class AntagPreferenceSelector : Control
+        private sealed class AntagPreferenceSelector : RequirementsSelector<AntagPrototype>
         {
-            public AntagPrototype Antag { get; }
-            private readonly CheckBox _checkBox;
-
+            // 0 is yes and 1 is no
             public bool Preference
             {
-                get => _checkBox.Pressed;
-                set => _checkBox.Pressed = value;
+                get => Options.SelectedValue == 0;
+                set => Options.Select((value && !Disabled) ? 0 : 1);
             }
 
             public event Action<bool>? PreferenceChanged;
 
-            public AntagPreferenceSelector(AntagPrototype antag)
+            public AntagPreferenceSelector(AntagPrototype proto)
+                : base(proto)
             {
-                Antag = antag;
-
-                _checkBox = new CheckBox {Text = Loc.GetString(antag.Name)};
-                _checkBox.OnToggled += OnCheckBoxToggled;
+                Options.OnItemSelected += args => PreferenceChanged?.Invoke(Preference);
 
-                if (antag.Description != null)
+                var items = new[]
                 {
-                    _checkBox.ToolTip = Loc.GetString(antag.Description);
-                }
-
-                AddChild(new BoxContainer
+                    ("humanoid-profile-editor-antag-preference-yes-button", 0),
+                    ("humanoid-profile-editor-antag-preference-no-button", 1)
+                };
+                var title = Loc.GetString(proto.Name);
+                var description = Loc.GetString(proto.Objective);
+                Setup(items, title, 250, description);
+
+                // immediately lock requirements if they arent met.
+                // another function checks Disabled after creating the selector so this has to be done now
+                var requirements = IoCManager.Resolve<JobRequirementsManager>();
+                if (proto.Requirements != null && !requirements.CheckRoleTime(proto.Requirements, out var reason))
                 {
-                    Orientation = LayoutOrientation.Horizontal,
-                    Children =
-                    {
-                        _checkBox
-                    }
-                });
-            }
-
-            private void OnCheckBoxToggled(BaseButton.ButtonToggledEventArgs args)
-            {
-                PreferenceChanged?.Invoke(Preference);
+                    LockRequirements(reason);
+                }
             }
         }
 
index b76cd8821fcc9f7c77a9adbd9e563b27783c3cad..92e38e35e0e4d58e9d09765978802d09861d28d2 100644 (file)
@@ -4,10 +4,12 @@
             Access="Public"
             Text="{Loc 'ghost-roles-window-request-role-button'}"
             StyleClasses="OpenRight"
-            HorizontalAlignment="Left"/>
+            HorizontalAlignment="Left"
+            SetWidth="150"/>
     <Button Name="FollowButton"
             Access="Public"
             Text="{Loc 'ghost-roles-window-follow-role-button'}"
             StyleClasses="OpenLeft"
-            HorizontalAlignment="Right"/>
+            HorizontalAlignment="Right"
+            SetWidth="150"/>
 </BoxContainer>
index 86bf1ddab3b1d4f5d8b0de179628e47286668ff4..5223894a07aff0061285544951df1b12577d5cba 100644 (file)
@@ -1,5 +1,7 @@
 <DefaultWindow xmlns="https://spacestation14.io"
-            Title="{Loc 'ghost-roles-window-title'}">
+            Title="{Loc 'ghost-roles-window-title'}"
+            MinSize="500 300"
+            SetSize="500 300">
     <BoxContainer Orientation="Vertical"
                   HorizontalExpand="True">
         <RichTextLabel Name="TopBanner" VerticalExpand="True"/>
index 6ad9e1aa9db7990a82993d4c6039d09bd1e8c674..d6a53adff2527f5a8465bdd8581b0376568dc1d3 100644 (file)
@@ -1,19 +1,25 @@
+using System.Numerics;
 using Content.Shared.Ghost.Roles;
 using Robust.Client.AutoGenerated;
+using Robust.Client.GameObjects;
 using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.CustomControls;
 using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Utility;
 
 namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
 {
     [GenerateTypedNameReferences]
     public sealed partial class GhostRolesEntry : BoxContainer
     {
+        private SpriteSystem _spriteSystem;
         public event Action<GhostRoleInfo>? OnRoleSelected;
         public event Action<GhostRoleInfo>? OnRoleFollow;
 
-        public GhostRolesEntry(string name, string description, IEnumerable<GhostRoleInfo> roles)
+        public GhostRolesEntry(string name, string description, bool hasAccess, FormattedMessage? reason, IEnumerable<GhostRoleInfo> roles, SpriteSystem spriteSystem)
         {
             RobustXamlLoader.Load(this);
+            _spriteSystem = spriteSystem;
 
             Title.Text = name;
             Description.SetMessage(description);
@@ -24,6 +30,27 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
                 button.RequestButton.OnPressed += _ => OnRoleSelected?.Invoke(role);
                 button.FollowButton.OnPressed += _ => OnRoleFollow?.Invoke(role);
 
+                if (!hasAccess)
+                {
+                    button.RequestButton.Disabled = true;
+
+                    if (reason != null && !reason.IsEmpty)
+                    {
+                        var tooltip = new Tooltip();
+                        tooltip.SetMessage(reason);
+                        button.RequestButton.TooltipSupplier = _ => tooltip;
+                    }
+
+                    button.RequestButton.AddChild(new TextureRect
+                    {
+                        TextureScale = new Vector2(0.4f, 0.4f),
+                        Stretch = TextureRect.StretchMode.KeepCentered,
+                        Texture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(new ("/Textures/Interface/Nano/lock.svg.192dpi.png"))),
+                        HorizontalExpand = true,
+                        HorizontalAlignment = HAlignment.Right,
+                    });
+                }
+
                 Buttons.AddChild(button);
             }
         }
index f52bf9fc4ba84557dd8d709822d89ee4a4299e18..8e72eafd97cb2069c2ebe8e04446b0e69487ad23 100644 (file)
@@ -1,8 +1,11 @@
 using System.Linq;
 using Content.Client.Eui;
+using Content.Client.Players.PlayTimeTracking;
 using Content.Shared.Eui;
 using Content.Shared.Ghost.Roles;
 using JetBrains.Annotations;
+using Robust.Client.GameObjects;
+using Robust.Shared.Utility;
 
 namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
 {
@@ -64,14 +67,26 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
             if (state is not GhostRolesEuiState ghostState) return;
             _window.ClearEntries();
 
+            var entityManager = IoCManager.Resolve<IEntityManager>();
+            var sysManager = entityManager.EntitySysManager;
+            var spriteSystem = sysManager.GetEntitySystem<SpriteSystem>();
+            var requirementsManager = IoCManager.Resolve<JobRequirementsManager>();
+
             var groupedRoles = ghostState.GhostRoles.GroupBy(
-                role => (role.Name, role.Description));
+                role => (role.Name, role.Description, role.Requirements));
             foreach (var group in groupedRoles)
             {
                 var name = group.Key.Name;
                 var description = group.Key.Description;
+                bool hasAccess = true;
+                FormattedMessage? reason;
+
+                if (!requirementsManager.CheckRoleTime(group.Key.Requirements, out reason))
+                {
+                    hasAccess = false;
+                }
 
-                _window.AddEntry(name, description, group);
+                _window.AddEntry(name, description, hasAccess, reason, group, spriteSystem);
             }
 
             var closeRulesWindow = ghostState.GhostRoles.All(role => role.Identifier != _windowRulesId);
index 8888b7da94106fcea0569681e2f70f7d76d3ef8d..c91269063edec09ae9c647510849db1617d37dc8 100644 (file)
@@ -1,6 +1,7 @@
 <DefaultWindow xmlns="https://spacestation14.io"
             Title="{Loc 'ghost-roles-window-title'}"
-            MinSize="375 275">
+            MinSize="450 400"
+            SetSize="400 500">
     <Label Name="NoRolesMessage"
            Text="{Loc 'ghost-roles-window-no-roles-available-label'}"
            VerticalAlignment="Top" />
index c7936ef73387e3f33cf4f3bc54d3fedb743c5d02..547d990e76f7e47c334aad81870209a71bbc02ab 100644 (file)
@@ -1,6 +1,8 @@
 using Content.Shared.Ghost.Roles;
 using Robust.Client.AutoGenerated;
+using Robust.Client.GameObjects;
 using Robust.Client.UserInterface.CustomControls;
+using Robust.Shared.Utility;
 
 namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
 {
@@ -16,11 +18,11 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
             EntryContainer.DisposeAllChildren();
         }
 
-        public void AddEntry(string name, string description, IEnumerable<GhostRoleInfo> roles)
+        public void AddEntry(string name, string description, bool hasAccess, FormattedMessage? reason, IEnumerable<GhostRoleInfo> roles, SpriteSystem spriteSystem)
         {
             NoRolesMessage.Visible = false;
 
-            var entry = new GhostRolesEntry(name, description, roles);
+            var entry = new GhostRolesEntry(name, description, hasAccess, reason, roles, spriteSystem);
             entry.OnRoleSelected += OnRoleRequested;
             entry.OnRoleFollow += OnRoleFollow;
             EntryContainer.AddChild(entry);
index 775156dc76da69badf448a54415ec99df7d9657a..93e7e9efaa650e413ee622df53287007b87937e9 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Mind.Commands;
+using Content.Shared.Roles;
 
 namespace Content.Server.Ghost.Roles.Components
 {
@@ -12,6 +13,9 @@ namespace Content.Server.Ghost.Roles.Components
 
         [DataField("rules")] private string _roleRules = "";
 
+        [DataField("requirements")]
+        public HashSet<JobRequirement>? Requirements;
+
         /// <summary>
         /// Whether the <see cref="MakeSentientCommand"/> should run on the mob.
         /// </summary>
index 38be3083792b15d2860d009133a1d57389f090ce..98a62d39707c2abed9eeac1f63dba27fdeb43175 100644 (file)
@@ -240,7 +240,7 @@ namespace Content.Server.Ghost.Roles
                 if (metaQuery.GetComponent(uid).EntityPaused)
                     continue;
 
-                roles.Add(new GhostRoleInfo {Identifier = id, Name = role.RoleName, Description = role.RoleDescription, Rules = role.RoleRules});
+                roles.Add(new GhostRoleInfo {Identifier = id, Name = role.RoleName, Description = role.RoleDescription, Rules = role.RoleRules, Requirements = role.Requirements});
             }
 
             return roles.ToArray();
index 31672c7a9e983d8ce76c12872da6aa1748f43bc7..8fbb931ca95873a28dbde7ec6548a7492481b00d 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Shared.Eui;
+using Content.Shared.Roles;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Ghost.Roles
@@ -10,6 +11,7 @@ namespace Content.Shared.Ghost.Roles
         public string Name { get; set; }
         public string Description { get; set; }
         public string Rules { get; set; }
+        public HashSet<JobRequirement>? Requirements { get; set; }
     }
 
     [NetSerializable, Serializable]
index 0bdb4d3a968b4e3651c13825360d4b35fc24f80b..91cb94050f9ca784ad7998167382dad6bd6760f1 100644 (file)
@@ -1,45 +1,46 @@
 using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
 
-namespace Content.Shared.Roles
+namespace Content.Shared.Roles;
+
+/// <summary>
+///     Describes information for a single antag.
+/// </summary>
+[Prototype("antag")]
+[Serializable, NetSerializable]
+public sealed class AntagPrototype : IPrototype
 {
+    [ViewVariables]
+    [IdDataField]
+    public string ID { get; private set; } = default!;
+
     /// <summary>
-    ///     Describes information for a single antag.
+    ///     The name of this antag as displayed to players.
     /// </summary>
-    [Prototype("antag")]
-    public sealed class AntagPrototype : IPrototype
-    {
-        [ViewVariables]
-        [IdDataField]
-        public string ID { get; private set; } = default!;
-
-        /// <summary>
-        ///     The name of this antag as displayed to players.
-        /// </summary>
-        [DataField("name")]
-        public string Name { get; private set; } = "";
+    [DataField("name")]
+    public string Name { get; private set; } = "";
 
-        /// <summary>
-        ///     The description of this antag shown in a tooltip.
-        /// </summary>
-        [DataField("description")]
-        public string? Description { get; private set; }
+    /// <summary>
+    ///     The antag's objective, shown in a tooltip in the antag preference menu or as a ghost role description.
+    /// </summary>
+    [DataField("objective", required: true)]
+    public string Objective { get; private set; } = "";
 
-        /// <summary>
-        ///     The antag's objective, displayed at round-start to the player.
-        /// </summary>
-        [DataField("objective")]
-        public string Objective { get; private set; } = "";
+    /// <summary>
+    ///     Whether or not the antag role is one of the bad guys.
+    /// </summary>
+    [DataField("antagonist")]
+    public bool Antagonist { get; private set; }
 
-        /// <summary>
-        ///     Whether or not the antag role is one of the bad guys.
-        /// </summary>
-        [DataField("antagonist")]
-        public bool Antagonist { get; private set; }
+    /// <summary>
+    ///     Whether or not the player can set the antag role in antag preferences.
+    /// </summary>
+    [DataField("setPreference")]
+    public bool SetPreference { get; private set; }
 
-        /// <summary>
-        ///     Whether or not the player can set the antag role in antag preferences.
-        /// </summary>
-        [DataField("setPreference")]
-        public bool SetPreference { get; private set; }
-    }
+    /// <summary>
+    ///     Requirements that must be met to opt in to this antag role.
+    /// </summary>
+    [DataField("requirements")]
+    public HashSet<JobRequirement>? Requirements;
 }
index dd54ca60cf5b43d67878defd63de47becfdaf1b0..fc3b759a9c20f33aa011dfdd1a4c46a13648287f 100644 (file)
@@ -3,6 +3,7 @@ using Content.Shared.Players.PlayTimeTracking;
 using Content.Shared.Roles.Jobs;
 using JetBrains.Annotations;
 using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
 using Robust.Shared.Utility;
 
@@ -12,9 +13,11 @@ namespace Content.Shared.Roles
     /// Abstract class for playtime and other requirements for role gates.
     /// </summary>
     [ImplicitDataDefinitionForInheritors]
+    [Serializable, NetSerializable]
     public abstract partial class JobRequirement{}
 
     [UsedImplicitly]
+    [Serializable, NetSerializable]
     public sealed partial class DepartmentTimeRequirement : JobRequirement
     {
         /// <summary>
@@ -39,6 +42,7 @@ namespace Content.Shared.Roles
     }
 
     [UsedImplicitly]
+    [Serializable, NetSerializable]
     public sealed partial class RoleTimeRequirement : JobRequirement
     {
         /// <summary>
@@ -55,6 +59,7 @@ namespace Content.Shared.Roles
     }
 
     [UsedImplicitly]
+    [Serializable, NetSerializable]
     public sealed partial class OverallPlaytimeRequirement : JobRequirement
     {
         /// <inheritdoc cref="DepartmentTimeRequirement.Time"/>
@@ -209,7 +214,6 @@ namespace Content.Shared.Roles
                             return false;
                         }
 
-
                         return true;
                     }
                 default:
index 2203fe38be99a67d9e0840555ddcc72a21fc0dda..6e279b4b28bb72cac3ebdaee9371bb320cdfe00b 100644 (file)
@@ -33,6 +33,8 @@ humanoid-profile-editor-preference-duffelbag = Duffelbag
 humanoid-profile-editor-jobs-amount-in-department-tooltip = Jobs in the {$departmentName} department
 humanoid-profile-editor-department-jobs-label = {$departmentName} jobs
 humanoid-profile-editor-antags-tab = Antags
+humanoid-profile-editor-antag-preference-yes-button = Yes
+humanoid-profile-editor-antag-preference-no-button = No
 humanoid-profile-editor-traits-tab = Traits
 humanoid-profile-editor-job-priority-high-button = High
 humanoid-profile-editor-job-priority-medium-button = Medium