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:
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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"/>
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user