Roundstart antag role restrictions revival (#20108)

Co-authored-by: Ray <vigersray@gmail.com>
Co-authored-by: deltanedas <@deltanedas:kde.org>
This commit is contained in:
deltanedas
2023-09-20 08:54:53 +01:00
committed by GitHub
parent 35246963c7
commit 4fbebb6917
14 changed files with 229 additions and 150 deletions

View File

@@ -97,21 +97,29 @@ public sealed class JobRequirementsManager
} }
var player = _playerManager.LocalPlayer?.Session; var player = _playerManager.LocalPlayer?.Session;
if (player == null) if (player == null)
return true; return true;
var reasonBuilder = new StringBuilder(); return CheckRoleTime(job.Requirements, out reason);
}
foreach (var requirement in job.Requirements) public bool CheckRoleTime(HashSet<JobRequirement>? requirements, [NotNullWhen(false)] out FormattedMessage? reason)
{
reason = null;
if (requirements == null)
return true;
var reasons = new List<string>();
foreach (var requirement in requirements)
{ {
if (JobRequirements.TryRequirementMet(requirement, _roles, out var jobReason, _entManager, _prototypes)) if (JobRequirements.TryRequirementMet(requirement, _roles, out var jobReason, _entManager, _prototypes))
continue; 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; return reason == null;
} }
} }

View File

@@ -393,13 +393,16 @@ namespace Content.Client.Preferences.UI
foreach (var antag in prototypeManager.EnumeratePrototypes<AntagPrototype>().OrderBy(a => Loc.GetString(a.Name))) foreach (var antag in prototypeManager.EnumeratePrototypes<AntagPrototype>().OrderBy(a => Loc.GetString(a.Name)))
{ {
if (!antag.SetPreference) if (!antag.SetPreference)
{
continue; continue;
}
var selector = new AntagPreferenceSelector(antag); var selector = new AntagPreferenceSelector(antag);
_antagList.AddChild(selector); _antagList.AddChild(selector);
_antagPreferences.Add(selector); _antagPreferences.Add(selector);
if (selector.Disabled)
{
Profile = Profile?.WithAntagPreference(antag.ID, false);
IsDirty = true;
}
selector.PreferenceChanged += preference => selector.PreferenceChanged += preference =>
{ {
@@ -587,19 +590,15 @@ namespace Content.Client.Preferences.UI
foreach (var jobSelector in _jobPriorities) foreach (var jobSelector in _jobPriorities)
{ {
// Sync other selectors with the same job in case of multiple department jobs // 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; jobSelector.Priority = priority;
} }
else if (priority == JobPriority.High && jobSelector.Priority == JobPriority.High)
{
// Lower any other high priorities to medium. // Lower any other high priorities to medium.
if (priority == JobPriority.High)
{
if (jobSelector.Job != selector.Job && jobSelector.Priority == JobPriority.High)
{
jobSelector.Priority = JobPriority.Medium; jobSelector.Priority = JobPriority.Medium;
Profile = Profile?.WithJobPriority(jobSelector.Job.ID, JobPriority.Medium); Profile = Profile?.WithJobPriority(jobSelector.Proto.ID, JobPriority.Medium);
}
} }
} }
}; };
@@ -1131,7 +1130,7 @@ namespace Content.Client.Preferences.UI
{ {
foreach (var prioritySelector in _jobPriorities) 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; 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; } public T Proto { get; }
private readonly RadioOptions<int> _optionButton; public bool Disabled => _lockStripe.Visible;
public JobPriority Priority
{
get => (JobPriority) _optionButton.SelectedValue;
set => _optionButton.SelectByValue((int) value);
}
public event Action<JobPriority>? PriorityChanged;
protected readonly RadioOptions<int> Options;
private StripeBack _lockStripe; private StripeBack _lockStripe;
private Label _requirementsLabel; 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, FirstButtonStyle = StyleBase.ButtonOpenRight,
ButtonStyle = StyleBase.ButtonOpenBoth, ButtonStyle = StyleBase.ButtonOpenBoth,
LastButtonStyle = StyleBase.ButtonOpenLeft LastButtonStyle = StyleBase.ButtonOpenLeft
}; };
//Override default radio option button width //Override default radio option button width
_optionButton.GenerateItem = GenerateButton; Options.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 => Options.OnItemSelected += args => Options.Select(args.Id);
{
_optionButton.Select(args.Id);
PriorityChanged?.Invoke(Priority);
};
var icon = new TextureRect
{
TextureScale = new Vector2(2, 2),
Stretch = TextureRect.StretchMode.KeepCentered
};
var jobIcon = prototypeManager.Index<StatusIconPrototype>(job.Icon);
icon.Texture = jobIcon.Icon.Frame0();
_requirementsLabel = new Label() _requirementsLabel = new Label()
{ {
@@ -1208,30 +1181,40 @@ namespace Content.Client.Preferences.UI
} }
}; };
_jobTitle = new Label() // Setup must be called after
}
/// <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)
{ {
Margin = new Thickness(5f,0,5f,0), foreach (var (text, value) in items)
Text = job.LocalizedName, {
MinSize = new Vector2(200, 0), Options.AddItem(Loc.GetString(text), value);
MouseFilter = MouseFilterMode.Stop }
var titleLabel = new Label()
{
Margin = new Thickness(5f, 0, 5f, 0),
Text = title,
MinSize = new Vector2(titleSize, 0),
MouseFilter = MouseFilterMode.Stop,
ToolTip = description
}; };
if (job.LocalizedDescription != null) var container = new BoxContainer
{
_jobTitle.ToolTip = job.LocalizedDescription;
}
AddChild(new BoxContainer
{ {
Orientation = LayoutOrientation.Horizontal, Orientation = LayoutOrientation.Horizontal,
Children = };
{
icon, if (icon != null)
_jobTitle, container.AddChild(icon);
_optionButton, container.AddChild(titleLabel);
_lockStripe, container.AddChild(Options);
} container.AddChild(_lockStripe);
});
AddChild(container);
} }
public void LockRequirements(FormattedMessage requirements) public void LockRequirements(FormattedMessage requirements)
@@ -1240,25 +1223,58 @@ namespace Content.Client.Preferences.UI
tooltip.SetMessage(requirements); tooltip.SetMessage(requirements);
_lockStripe.TooltipSupplier = _ => tooltip; _lockStripe.TooltipSupplier = _ => tooltip;
_lockStripe.Visible = true; _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 // TODO: Subscribe to roletimers event. I am too lazy to do this RN But I doubt most people will notice fn
public void UnlockRequirements() public void UnlockRequirements()
{ {
_requirementsLabel.Visible = false;
_lockStripe.Visible = false; _lockStripe.Visible = false;
_optionButton.Visible = true; Options.Visible = true;
} }
private Button GenerateButton(string text, int value) private Button GenerateButton(string text, int value)
{ {
var btn = new Button return new Button
{ {
Text = text, Text = text,
MinWidth = 90 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) foreach (var preferenceSelector in _antagPreferences)
{ {
var antagId = preferenceSelector.Antag.ID; var antagId = preferenceSelector.Proto.ID;
var preference = Profile?.AntagPreferences.Contains(antagId) ?? false; var preference = Profile?.AntagPreferences.Contains(antagId) ?? false;
preferenceSelector.Preference = preference; 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; } // 0 is yes and 1 is no
private readonly CheckBox _checkBox;
public bool Preference public bool Preference
{ {
get => _checkBox.Pressed; get => Options.SelectedValue == 0;
set => _checkBox.Pressed = value; set => Options.Select((value && !Disabled) ? 0 : 1);
} }
public event Action<bool>? PreferenceChanged; public event Action<bool>? PreferenceChanged;
public AntagPreferenceSelector(AntagPrototype antag) public AntagPreferenceSelector(AntagPrototype proto)
: base(proto)
{ {
Antag = antag; Options.OnItemSelected += args => PreferenceChanged?.Invoke(Preference);
_checkBox = new CheckBox {Text = Loc.GetString(antag.Name)}; var items = new[]
_checkBox.OnToggled += OnCheckBoxToggled;
if (antag.Description != null)
{ {
_checkBox.ToolTip = Loc.GetString(antag.Description); ("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))
{
LockRequirements(reason);
} }
AddChild(new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
Children =
{
_checkBox
}
});
}
private void OnCheckBoxToggled(BaseButton.ButtonToggledEventArgs args)
{
PreferenceChanged?.Invoke(Preference);
} }
} }

View File

@@ -4,10 +4,12 @@
Access="Public" Access="Public"
Text="{Loc 'ghost-roles-window-request-role-button'}" Text="{Loc 'ghost-roles-window-request-role-button'}"
StyleClasses="OpenRight" StyleClasses="OpenRight"
HorizontalAlignment="Left"/> HorizontalAlignment="Left"
SetWidth="150"/>
<Button Name="FollowButton" <Button Name="FollowButton"
Access="Public" Access="Public"
Text="{Loc 'ghost-roles-window-follow-role-button'}" Text="{Loc 'ghost-roles-window-follow-role-button'}"
StyleClasses="OpenLeft" StyleClasses="OpenLeft"
HorizontalAlignment="Right"/> HorizontalAlignment="Right"
SetWidth="150"/>
</BoxContainer> </BoxContainer>

View File

@@ -1,5 +1,7 @@
<DefaultWindow xmlns="https://spacestation14.io" <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" <BoxContainer Orientation="Vertical"
HorizontalExpand="True"> HorizontalExpand="True">
<RichTextLabel Name="TopBanner" VerticalExpand="True"/> <RichTextLabel Name="TopBanner" VerticalExpand="True"/>

View File

@@ -1,19 +1,25 @@
using System.Numerics;
using Content.Shared.Ghost.Roles; using Content.Shared.Ghost.Roles;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
{ {
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class GhostRolesEntry : BoxContainer public sealed partial class GhostRolesEntry : BoxContainer
{ {
private SpriteSystem _spriteSystem;
public event Action<GhostRoleInfo>? OnRoleSelected; public event Action<GhostRoleInfo>? OnRoleSelected;
public event Action<GhostRoleInfo>? OnRoleFollow; 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); RobustXamlLoader.Load(this);
_spriteSystem = spriteSystem;
Title.Text = name; Title.Text = name;
Description.SetMessage(description); Description.SetMessage(description);
@@ -24,6 +30,27 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
button.RequestButton.OnPressed += _ => OnRoleSelected?.Invoke(role); button.RequestButton.OnPressed += _ => OnRoleSelected?.Invoke(role);
button.FollowButton.OnPressed += _ => OnRoleFollow?.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); Buttons.AddChild(button);
} }
} }

View File

@@ -1,8 +1,11 @@
using System.Linq; using System.Linq;
using Content.Client.Eui; using Content.Client.Eui;
using Content.Client.Players.PlayTimeTracking;
using Content.Shared.Eui; using Content.Shared.Eui;
using Content.Shared.Ghost.Roles; using Content.Shared.Ghost.Roles;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Shared.Utility;
namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles 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; if (state is not GhostRolesEuiState ghostState) return;
_window.ClearEntries(); _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( var groupedRoles = ghostState.GhostRoles.GroupBy(
role => (role.Name, role.Description)); role => (role.Name, role.Description, role.Requirements));
foreach (var group in groupedRoles) foreach (var group in groupedRoles)
{ {
var name = group.Key.Name; var name = group.Key.Name;
var description = group.Key.Description; var description = group.Key.Description;
bool hasAccess = true;
FormattedMessage? reason;
_window.AddEntry(name, description, group); if (!requirementsManager.CheckRoleTime(group.Key.Requirements, out reason))
{
hasAccess = false;
}
_window.AddEntry(name, description, hasAccess, reason, group, spriteSystem);
} }
var closeRulesWindow = ghostState.GhostRoles.All(role => role.Identifier != _windowRulesId); var closeRulesWindow = ghostState.GhostRoles.All(role => role.Identifier != _windowRulesId);

View File

@@ -1,6 +1,7 @@
<DefaultWindow xmlns="https://spacestation14.io" <DefaultWindow xmlns="https://spacestation14.io"
Title="{Loc 'ghost-roles-window-title'}" Title="{Loc 'ghost-roles-window-title'}"
MinSize="375 275"> MinSize="450 400"
SetSize="400 500">
<Label Name="NoRolesMessage" <Label Name="NoRolesMessage"
Text="{Loc 'ghost-roles-window-no-roles-available-label'}" Text="{Loc 'ghost-roles-window-no-roles-available-label'}"
VerticalAlignment="Top" /> VerticalAlignment="Top" />

View File

@@ -1,6 +1,8 @@
using Content.Shared.Ghost.Roles; using Content.Shared.Ghost.Roles;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Utility;
namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
{ {
@@ -16,11 +18,11 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
EntryContainer.DisposeAllChildren(); 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; NoRolesMessage.Visible = false;
var entry = new GhostRolesEntry(name, description, roles); var entry = new GhostRolesEntry(name, description, hasAccess, reason, roles, spriteSystem);
entry.OnRoleSelected += OnRoleRequested; entry.OnRoleSelected += OnRoleRequested;
entry.OnRoleFollow += OnRoleFollow; entry.OnRoleFollow += OnRoleFollow;
EntryContainer.AddChild(entry); EntryContainer.AddChild(entry);

View File

@@ -1,4 +1,5 @@
using Content.Server.Mind.Commands; using Content.Server.Mind.Commands;
using Content.Shared.Roles;
namespace Content.Server.Ghost.Roles.Components namespace Content.Server.Ghost.Roles.Components
{ {
@@ -12,6 +13,9 @@ namespace Content.Server.Ghost.Roles.Components
[DataField("rules")] private string _roleRules = ""; [DataField("rules")] private string _roleRules = "";
[DataField("requirements")]
public HashSet<JobRequirement>? Requirements;
/// <summary> /// <summary>
/// Whether the <see cref="MakeSentientCommand"/> should run on the mob. /// Whether the <see cref="MakeSentientCommand"/> should run on the mob.
/// </summary> /// </summary>

View File

@@ -240,7 +240,7 @@ namespace Content.Server.Ghost.Roles
if (metaQuery.GetComponent(uid).EntityPaused) if (metaQuery.GetComponent(uid).EntityPaused)
continue; 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(); return roles.ToArray();

View File

@@ -1,4 +1,5 @@
using Content.Shared.Eui; using Content.Shared.Eui;
using Content.Shared.Roles;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.Ghost.Roles namespace Content.Shared.Ghost.Roles
@@ -10,6 +11,7 @@ namespace Content.Shared.Ghost.Roles
public string Name { get; set; } public string Name { get; set; }
public string Description { get; set; } public string Description { get; set; }
public string Rules { get; set; } public string Rules { get; set; }
public HashSet<JobRequirement>? Requirements { get; set; }
} }
[NetSerializable, Serializable] [NetSerializable, Serializable]

View File

@@ -1,13 +1,15 @@
using Robust.Shared.Prototypes; 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
{ {
/// <summary>
/// Describes information for a single antag.
/// </summary>
[Prototype("antag")]
public sealed class AntagPrototype : IPrototype
{
[ViewVariables] [ViewVariables]
[IdDataField] [IdDataField]
public string ID { get; private set; } = default!; public string ID { get; private set; } = default!;
@@ -19,15 +21,9 @@ namespace Content.Shared.Roles
public string Name { get; private set; } = ""; public string Name { get; private set; } = "";
/// <summary> /// <summary>
/// The description of this antag shown in a tooltip. /// The antag's objective, shown in a tooltip in the antag preference menu or as a ghost role description.
/// </summary> /// </summary>
[DataField("description")] [DataField("objective", required: true)]
public string? Description { get; private set; }
/// <summary>
/// The antag's objective, displayed at round-start to the player.
/// </summary>
[DataField("objective")]
public string Objective { get; private set; } = ""; public string Objective { get; private set; } = "";
/// <summary> /// <summary>
@@ -41,5 +37,10 @@ namespace Content.Shared.Roles
/// </summary> /// </summary>
[DataField("setPreference")] [DataField("setPreference")]
public bool SetPreference { get; private set; } 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;
} }

View File

@@ -3,6 +3,7 @@ using Content.Shared.Players.PlayTimeTracking;
using Content.Shared.Roles.Jobs; using Content.Shared.Roles.Jobs;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -12,9 +13,11 @@ namespace Content.Shared.Roles
/// Abstract class for playtime and other requirements for role gates. /// Abstract class for playtime and other requirements for role gates.
/// </summary> /// </summary>
[ImplicitDataDefinitionForInheritors] [ImplicitDataDefinitionForInheritors]
[Serializable, NetSerializable]
public abstract partial class JobRequirement{} public abstract partial class JobRequirement{}
[UsedImplicitly] [UsedImplicitly]
[Serializable, NetSerializable]
public sealed partial class DepartmentTimeRequirement : JobRequirement public sealed partial class DepartmentTimeRequirement : JobRequirement
{ {
/// <summary> /// <summary>
@@ -39,6 +42,7 @@ namespace Content.Shared.Roles
} }
[UsedImplicitly] [UsedImplicitly]
[Serializable, NetSerializable]
public sealed partial class RoleTimeRequirement : JobRequirement public sealed partial class RoleTimeRequirement : JobRequirement
{ {
/// <summary> /// <summary>
@@ -55,6 +59,7 @@ namespace Content.Shared.Roles
} }
[UsedImplicitly] [UsedImplicitly]
[Serializable, NetSerializable]
public sealed partial class OverallPlaytimeRequirement : JobRequirement public sealed partial class OverallPlaytimeRequirement : JobRequirement
{ {
/// <inheritdoc cref="DepartmentTimeRequirement.Time"/> /// <inheritdoc cref="DepartmentTimeRequirement.Time"/>
@@ -209,7 +214,6 @@ namespace Content.Shared.Roles
return false; return false;
} }
return true; return true;
} }
default: default:

View 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-jobs-amount-in-department-tooltip = Jobs in the {$departmentName} department
humanoid-profile-editor-department-jobs-label = {$departmentName} jobs humanoid-profile-editor-department-jobs-label = {$departmentName} jobs
humanoid-profile-editor-antags-tab = Antags 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-traits-tab = Traits
humanoid-profile-editor-job-priority-high-button = High humanoid-profile-editor-job-priority-high-button = High
humanoid-profile-editor-job-priority-medium-button = Medium humanoid-profile-editor-job-priority-medium-button = Medium