From 7a57a2ba1133b23d03e18233af515b7d85e731ca Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:51:51 +0300 Subject: [PATCH 01/10] refactor JobRequirements --- .../Lobby/UI/HumanoidProfileEditor.xaml.cs | 2 +- .../JobRequirementsManager.cs | 10 +- .../Ghost/Controls/Roles/GhostRolesEui.cs | 2 +- .../Effects/JobRequirementLoadoutEffect.cs | 7 +- .../DepartmentTimeRequirement.cs | 75 ++++++ .../OverallPlaytimeRequirement.cs | 48 ++++ .../JobRequirement/RoleTimeRequirement.cs | 71 +++++ Content.Shared/Roles/JobRequirements.cs | 250 +++--------------- 8 files changed, 240 insertions(+), 225 deletions(-) create mode 100644 Content.Shared/Roles/JobRequirement/DepartmentTimeRequirement.cs create mode 100644 Content.Shared/Roles/JobRequirement/OverallPlaytimeRequirement.cs create mode 100644 Content.Shared/Roles/JobRequirement/RoleTimeRequirement.cs diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs index 87ef41c0b738..1a3bd508ca0d 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs @@ -634,7 +634,7 @@ public void RefreshAntags() selector.Select(Profile?.AntagPreferences.Contains(antag.ID) == true ? 0 : 1); var requirements = _entManager.System().GetAntagRequirement(antag); - if (!_requirements.CheckRoleTime(requirements, out var reason)) + if (!_requirements.CheckRoleRequirements(requirements, out var reason)) { selector.LockRequirements(reason); Profile = Profile?.WithAntagPreference(antag.ID, false); diff --git a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs index bd4ac877dbbf..eae9acb58eb1 100644 --- a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs +++ b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs @@ -106,16 +106,16 @@ public bool IsAllowed(JobPrototype job, [NotNullWhen(false)] out FormattedMessag if (player == null) return true; - return CheckRoleTime(job, out reason); + return CheckRoleRequirements(job, out reason); } - public bool CheckRoleTime(JobPrototype job, [NotNullWhen(false)] out FormattedMessage? reason) + public bool CheckRoleRequirements(JobPrototype job, [NotNullWhen(false)] out FormattedMessage? reason) { var reqs = _entManager.System().GetJobRequirement(job); - return CheckRoleTime(reqs, out reason); + return CheckRoleRequirements(reqs, out reason); } - public bool CheckRoleTime(HashSet? requirements, [NotNullWhen(false)] out FormattedMessage? reason) + public bool CheckRoleRequirements(HashSet? requirements, [NotNullWhen(false)] out FormattedMessage? reason) { reason = null; @@ -125,7 +125,7 @@ public bool CheckRoleTime(HashSet? requirements, [NotNullWhen(fa var reasons = new List(); foreach (var requirement in requirements) { - if (JobRequirements.TryRequirementMet(requirement, _roles, out var jobReason, _entManager, _prototypes)) + if (requirement.Check(_entManager, _prototypes, _roles, out var jobReason)) continue; reasons.Add(jobReason.ToMarkup()); diff --git a/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesEui.cs b/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesEui.cs index 33358a68a4d4..8453d65cf6f4 100644 --- a/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesEui.cs +++ b/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesEui.cs @@ -93,7 +93,7 @@ public override void HandleState(EuiStateBase state) bool hasAccess = true; FormattedMessage? reason; - if (!requirementsManager.CheckRoleTime(group.Key.Requirements, out reason)) + if (!requirementsManager.CheckRoleRequirements(group.Key.Requirements, out reason)) { hasAccess = false; } diff --git a/Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs b/Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs index 4a40e2c65e64..f819398f4251 100644 --- a/Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs +++ b/Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs @@ -25,8 +25,9 @@ public override bool Validate(HumanoidCharacterProfile profile, RoleLoadout load var manager = collection.Resolve(); var playtimes = manager.GetPlayTimes(session); - return JobRequirements.TryRequirementMet(Requirement, playtimes, out reason, - collection.Resolve(), - collection.Resolve()); + return Requirement.Check(collection.Resolve(), + collection.Resolve(), + playtimes, + out reason); } } diff --git a/Content.Shared/Roles/JobRequirement/DepartmentTimeRequirement.cs b/Content.Shared/Roles/JobRequirement/DepartmentTimeRequirement.cs new file mode 100644 index 000000000000..f62a8debaf1d --- /dev/null +++ b/Content.Shared/Roles/JobRequirement/DepartmentTimeRequirement.cs @@ -0,0 +1,75 @@ +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Content.Shared.Roles; + +[UsedImplicitly] +[Serializable, NetSerializable] +public sealed partial class DepartmentTimeRequirement : JobRequirement +{ + /// + /// Which department needs the required amount of time. + /// + [DataField] + public ProtoId Department = default!; + + /// + /// How long (in seconds) this requirement is. + /// + [DataField] + public TimeSpan Time; + + public override bool Check(IEntityManager entManager, + IPrototypeManager protoManager, + IReadOnlyDictionary playTimes, + [NotNullWhen(false)] out FormattedMessage? reason) + { + reason = new FormattedMessage(); + var playtime = TimeSpan.Zero; + + // Check all jobs' departments + var department = protoManager.Index(Department); + var jobs = department.Roles; + string proto; + + // Check all jobs' playtime + foreach (var other in jobs) + { + // The schema is stored on the Job role but we want to explode if the timer isn't found anyway. + proto = protoManager.Index(other).PlayTimeTracker; + + playTimes.TryGetValue(proto, out var otherTime); + playtime += otherTime; + } + + var deptDiff = Time.TotalMinutes - playtime.TotalMinutes; + + if (!Inverted) + { + if (deptDiff <= 0) + return true; + + reason = FormattedMessage.FromMarkupPermissive(Loc.GetString( + "role-timer-department-insufficient", + ("time", Math.Ceiling(deptDiff)), + ("department", Loc.GetString(Department)), + ("departmentColor", department.Color.ToHex()))); + return false; + } + + if (deptDiff <= 0) + { + reason = FormattedMessage.FromMarkupPermissive(Loc.GetString( + "role-timer-department-too-high", + ("time", -deptDiff), + ("department", Loc.GetString(Department)), + ("departmentColor", department.Color.ToHex()))); + return false; + } + + return true; + } +} diff --git a/Content.Shared/Roles/JobRequirement/OverallPlaytimeRequirement.cs b/Content.Shared/Roles/JobRequirement/OverallPlaytimeRequirement.cs new file mode 100644 index 000000000000..88e515663e3a --- /dev/null +++ b/Content.Shared/Roles/JobRequirement/OverallPlaytimeRequirement.cs @@ -0,0 +1,48 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Players.PlayTimeTracking; +using JetBrains.Annotations; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Content.Shared.Roles; + +[UsedImplicitly] +[Serializable, NetSerializable] +public sealed partial class OverallPlaytimeRequirement : JobRequirement +{ + /// + [DataField] + public TimeSpan Time; + + public override bool Check(IEntityManager entManager, + IPrototypeManager protoManager, + IReadOnlyDictionary playTimes, + [NotNullWhen(false)] out FormattedMessage? reason) + { + reason = new FormattedMessage(); + + var overallTime = playTimes.GetValueOrDefault(PlayTimeTrackingShared.TrackerOverall); + var overallDiff = Time.TotalMinutes - overallTime.TotalMinutes; + + if (!Inverted) + { + if (overallDiff <= 0 || overallTime >= Time) + return true; + + reason = FormattedMessage.FromMarkupPermissive(Loc.GetString( + "role-timer-overall-insufficient", + ("time", Math.Ceiling(overallDiff)))); + return false; + } + + if (overallDiff <= 0 || overallTime >= Time) + { + reason = FormattedMessage.FromMarkupPermissive(Loc.GetString("role-timer-overall-too-high", + ("time", -overallDiff))); + return false; + } + + return true; + } +} diff --git a/Content.Shared/Roles/JobRequirement/RoleTimeRequirement.cs b/Content.Shared/Roles/JobRequirement/RoleTimeRequirement.cs new file mode 100644 index 000000000000..12d3142328a4 --- /dev/null +++ b/Content.Shared/Roles/JobRequirement/RoleTimeRequirement.cs @@ -0,0 +1,71 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Players.PlayTimeTracking; +using Content.Shared.Roles.Jobs; +using JetBrains.Annotations; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Content.Shared.Roles; + +[UsedImplicitly] +[Serializable, NetSerializable] +public sealed partial class RoleTimeRequirement : JobRequirement +{ + /// + /// What particular role they need the time requirement with. + /// + [DataField] + public ProtoId Role = default!; + + /// + [DataField] + public TimeSpan Time; + + public override bool Check(IEntityManager entManager, + IPrototypeManager protoManager, + IReadOnlyDictionary playTimes, + [NotNullWhen(false)] out FormattedMessage? reason) + { + reason = new FormattedMessage(); + + string proto = Role; + + playTimes.TryGetValue(proto, out var roleTime); + var roleDiff = Time.TotalMinutes - roleTime.TotalMinutes; + var departmentColor = Color.Yellow; + + if (entManager.EntitySysManager.TryGetEntitySystem(out SharedJobSystem? jobSystem)) + { + var jobProto = jobSystem.GetJobPrototype(proto); + + if (jobSystem.TryGetDepartment(jobProto, out var departmentProto)) + departmentColor = departmentProto.Color; + } + + if (!Inverted) + { + if (roleDiff <= 0) + return true; + + reason = FormattedMessage.FromMarkupPermissive(Loc.GetString( + "role-timer-role-insufficient", + ("time", Math.Ceiling(roleDiff)), + ("job", Loc.GetString(proto)), + ("departmentColor", departmentColor.ToHex()))); + return false; + } + + if (roleDiff <= 0) + { + reason = FormattedMessage.FromMarkupPermissive(Loc.GetString( + "role-timer-role-too-high", + ("time", -roleDiff), + ("job", Loc.GetString(proto)), + ("departmentColor", departmentColor.ToHex()))); + return false; + } + + return true; + } +} diff --git a/Content.Shared/Roles/JobRequirements.cs b/Content.Shared/Roles/JobRequirements.cs index c9d66fcf9186..cb09dc9396f1 100644 --- a/Content.Shared/Roles/JobRequirements.cs +++ b/Content.Shared/Roles/JobRequirements.cs @@ -1,228 +1,48 @@ using System.Diagnostics.CodeAnalysis; -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; -namespace Content.Shared.Roles -{ - /// - /// Abstract class for playtime and other requirements for role gates. - /// - [ImplicitDataDefinitionForInheritors] - [Serializable, NetSerializable] - public abstract partial class JobRequirement{} - - [UsedImplicitly] - [Serializable, NetSerializable] - public sealed partial class DepartmentTimeRequirement : JobRequirement - { - /// - /// Which department needs the required amount of time. - /// - [DataField("department", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Department = default!; - - /// - /// How long (in seconds) this requirement is. - /// - [DataField("time")] public TimeSpan Time; - - /// - /// If true, requirement will return false if playtime above the specified time. - /// - /// - /// False by default.
- /// True for invert general requirement - ///
- [DataField("inverted")] public bool Inverted; - } - - [UsedImplicitly] - [Serializable, NetSerializable] - public sealed partial class RoleTimeRequirement : JobRequirement - { - /// - /// What particular role they need the time requirement with. - /// - [DataField("role", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Role = default!; - - /// - [DataField("time")] public TimeSpan Time; - - /// - [DataField("inverted")] public bool Inverted; - } +namespace Content.Shared.Roles; - [UsedImplicitly] - [Serializable, NetSerializable] - public sealed partial class OverallPlaytimeRequirement : JobRequirement - { - /// - [DataField("time")] public TimeSpan Time; - - /// - [DataField("inverted")] public bool Inverted; - } - - public static class JobRequirements +public static class JobRequirements +{ + public static bool TryRequirementsMet( + JobPrototype job, + IReadOnlyDictionary playTimes, + [NotNullWhen(false)] out FormattedMessage? reason, + IEntityManager entManager, + IPrototypeManager protoManager) { - public static bool TryRequirementsMet( - JobPrototype job, - IReadOnlyDictionary playTimes, - [NotNullWhen(false)] out FormattedMessage? reason, - IEntityManager entManager, - IPrototypeManager prototypes) - { - var sys = entManager.System(); - var requirements = sys.GetJobRequirement(job); - reason = null; - if (requirements == null) - return true; - - foreach (var requirement in requirements) - { - if (!TryRequirementMet(requirement, playTimes, out reason, entManager, prototypes)) - return false; - } - + var sys = entManager.System(); + var requirements = sys.GetJobRequirement(job); + reason = null; + if (requirements == null) return true; - } - /// - /// Returns a string with the reason why a particular requirement may not be met. - /// - public static bool TryRequirementMet( - JobRequirement requirement, - IReadOnlyDictionary playTimes, - [NotNullWhen(false)] out FormattedMessage? reason, - IEntityManager entManager, - IPrototypeManager prototypes) + foreach (var requirement in requirements) { - reason = null; - - switch (requirement) - { - case DepartmentTimeRequirement deptRequirement: - var playtime = TimeSpan.Zero; - - // Check all jobs' departments - var department = prototypes.Index(deptRequirement.Department); - var jobs = department.Roles; - string proto; - - // Check all jobs' playtime - foreach (var other in jobs) - { - // The schema is stored on the Job role but we want to explode if the timer isn't found anyway. - proto = prototypes.Index(other).PlayTimeTracker; - - playTimes.TryGetValue(proto, out var otherTime); - playtime += otherTime; - } - - var deptDiff = deptRequirement.Time.TotalMinutes - playtime.TotalMinutes; - - if (!deptRequirement.Inverted) - { - if (deptDiff <= 0) - return true; - - reason = FormattedMessage.FromMarkupPermissive(Loc.GetString( - "role-timer-department-insufficient", - ("time", Math.Ceiling(deptDiff)), - ("department", Loc.GetString(deptRequirement.Department)), - ("departmentColor", department.Color.ToHex()))); - return false; - } - else - { - if (deptDiff <= 0) - { - reason = FormattedMessage.FromMarkupPermissive(Loc.GetString( - "role-timer-department-too-high", - ("time", -deptDiff), - ("department", Loc.GetString(deptRequirement.Department)), - ("departmentColor", department.Color.ToHex()))); - return false; - } - - return true; - } - - case OverallPlaytimeRequirement overallRequirement: - var overallTime = playTimes.GetValueOrDefault(PlayTimeTrackingShared.TrackerOverall); - var overallDiff = overallRequirement.Time.TotalMinutes - overallTime.TotalMinutes; - - if (!overallRequirement.Inverted) - { - if (overallDiff <= 0 || overallTime >= overallRequirement.Time) - return true; - - reason = FormattedMessage.FromMarkupPermissive(Loc.GetString( - "role-timer-overall-insufficient", - ("time", Math.Ceiling(overallDiff)))); - return false; - } - else - { - if (overallDiff <= 0 || overallTime >= overallRequirement.Time) - { - reason = FormattedMessage.FromMarkupPermissive(Loc.GetString("role-timer-overall-too-high", ("time", -overallDiff))); - return false; - } - - return true; - } - - case RoleTimeRequirement roleRequirement: - proto = roleRequirement.Role; - - playTimes.TryGetValue(proto, out var roleTime); - var roleDiff = roleRequirement.Time.TotalMinutes - roleTime.TotalMinutes; - var departmentColor = Color.Yellow; - - if (entManager.EntitySysManager.TryGetEntitySystem(out SharedJobSystem? jobSystem)) - { - var jobProto = jobSystem.GetJobPrototype(proto); - - if (jobSystem.TryGetDepartment(jobProto, out var departmentProto)) - departmentColor = departmentProto.Color; - } - - if (!roleRequirement.Inverted) - { - if (roleDiff <= 0) - return true; - - reason = FormattedMessage.FromMarkupPermissive(Loc.GetString( - "role-timer-role-insufficient", - ("time", Math.Ceiling(roleDiff)), - ("job", Loc.GetString(proto)), - ("departmentColor", departmentColor.ToHex()))); - return false; - } - else - { - if (roleDiff <= 0) - { - reason = FormattedMessage.FromMarkupPermissive(Loc.GetString( - "role-timer-role-too-high", - ("time", -roleDiff), - ("job", Loc.GetString(proto)), - ("departmentColor", departmentColor.ToHex()))); - return false; - } - - return true; - } - default: - throw new NotImplementedException(); - } + if (!requirement.Check(entManager, protoManager, playTimes, out reason)) + return false; } + + return true; } } + +/// +/// Abstract class for playtime and other requirements for role gates. +/// +[ImplicitDataDefinitionForInheritors] +[Serializable, NetSerializable] +public abstract partial class JobRequirement +{ + [DataField] + public bool Inverted; + + public abstract bool Check( + IEntityManager entManager, + IPrototypeManager protoManager, + IReadOnlyDictionary playTimes, + [NotNullWhen(false)] out FormattedMessage? reason); +} From ee07839a38960a88931f9466f4ebe1ff475ec422 Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Thu, 25 Jul 2024 12:54:21 +0300 Subject: [PATCH 02/10] add profile support --- .../JobRequirementsManager.cs | 7 ++- .../PlayTimeTrackingSystem.cs | 14 ++++-- .../Effects/JobRequirementLoadoutEffect.cs | 1 + .../Roles/JobRequirement/AgeRequirement.cs | 47 +++++++++++++++++++ .../DepartmentTimeRequirement.cs | 2 + .../OverallPlaytimeRequirement.cs | 2 + .../JobRequirement/RoleTimeRequirement.cs | 2 + Content.Shared/Roles/JobRequirements.cs | 7 ++- ...{role-timers.ftl => role-requirements.ftl} | 2 + .../Prototypes/Roles/Jobs/Command/captain.yml | 2 + .../Roles/Jobs/Command/head_of_personnel.yml | 2 + .../Roles/Jobs/Engineering/chief_engineer.yml | 2 + .../Jobs/Medical/chief_medical_officer.yml | 2 + .../Roles/Jobs/Science/research_director.yml | 2 + .../Roles/Jobs/Security/head_of_security.yml | 2 + 15 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 Content.Shared/Roles/JobRequirement/AgeRequirement.cs rename Resources/Locale/en-US/job/{role-timers.ftl => role-requirements.ftl} (84%) diff --git a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs index eae9acb58eb1..4c8d30391c73 100644 --- a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs +++ b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs @@ -1,8 +1,10 @@ using System.Diagnostics.CodeAnalysis; +using Content.Client.Lobby; using Content.Shared.CCVar; using Content.Shared.Players; using Content.Shared.Players.JobWhitelist; using Content.Shared.Players.PlayTimeTracking; +using Content.Shared.Preferences; using Content.Shared.Roles; using Robust.Client; using Robust.Client.Player; @@ -22,6 +24,7 @@ public sealed class JobRequirementsManager : ISharedPlaytimeManager [Dependency] private readonly IEntityManager _entManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPrototypeManager _prototypes = default!; + [Dependency] private readonly IClientPreferencesManager _preferencesManager = default!; private readonly Dictionary _roles = new(); private readonly List _roleBans = new(); @@ -122,10 +125,12 @@ public bool CheckRoleRequirements(HashSet? requirements, [NotNul if (requirements == null || !_cfg.GetCVar(CCVars.GameRoleTimers)) return true; + var profile = (HumanoidCharacterProfile?) _preferencesManager.Preferences?.SelectedCharacter; + var reasons = new List(); foreach (var requirement in requirements) { - if (requirement.Check(_entManager, _prototypes, _roles, out var jobReason)) + if (requirement.Check(_entManager, _prototypes, profile, _roles, out var jobReason)) continue; reasons.Add(jobReason.ToMarkup()); diff --git a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs index ea6f0ad3f455..bfbe1f5724f3 100644 --- a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs +++ b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs @@ -6,6 +6,7 @@ using Content.Server.GameTicking; using Content.Server.GameTicking.Events; using Content.Server.Mind; +using Content.Server.Preferences.Managers; using Content.Server.Station.Events; using Content.Shared.CCVar; using Content.Shared.GameTicking; @@ -13,6 +14,7 @@ using Content.Shared.Mobs.Components; using Content.Shared.Players; using Content.Shared.Players.PlayTimeTracking; +using Content.Shared.Preferences; using Content.Shared.Roles; using Robust.Server.Player; using Robust.Shared.Configuration; @@ -35,6 +37,7 @@ public sealed class PlayTimeTrackingSystem : EntitySystem [Dependency] private readonly MindSystem _minds = default!; [Dependency] private readonly PlayTimeTrackingManager _tracking = default!; [Dependency] private readonly IAdminManager _adminManager = default!; + [Dependency] private readonly IServerPreferencesManager _preferencesManager = default!; public override void Initialize() { @@ -205,8 +208,9 @@ public bool IsAllowed(ICommonSession player, string role) Log.Error($"Unable to check playtimes {Environment.StackTrace}"); playTimes = new Dictionary(); } + var profile = (HumanoidCharacterProfile?) _preferencesManager.GetPreferences(player.UserId).SelectedCharacter; - return JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes); + return JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes, profile); } public HashSet> GetDisallowedJobs(ICommonSession player) @@ -221,9 +225,11 @@ public HashSet> GetDisallowedJobs(ICommonSession player) playTimes = new Dictionary(); } + var profile = (HumanoidCharacterProfile?) _preferencesManager.GetPreferences(player.UserId).SelectedCharacter; + foreach (var job in _prototypes.EnumeratePrototypes()) { - if (JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes)) + if (JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes, profile)) roles.Add(job.ID); } @@ -243,10 +249,12 @@ public void RemoveDisallowedJobs(NetUserId userId, List> j playTimes ??= new Dictionary(); } + var profile = (HumanoidCharacterProfile?) _preferencesManager.GetPreferences(userId).SelectedCharacter; + for (var i = 0; i < jobs.Count; i++) { if (_prototypes.TryIndex(jobs[i], out var job) - && JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes)) + && JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes, profile)) { continue; } diff --git a/Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs b/Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs index f819398f4251..2f7e7b7c4877 100644 --- a/Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs +++ b/Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs @@ -27,6 +27,7 @@ public override bool Validate(HumanoidCharacterProfile profile, RoleLoadout load var playtimes = manager.GetPlayTimes(session); return Requirement.Check(collection.Resolve(), collection.Resolve(), + profile, playtimes, out reason); } diff --git a/Content.Shared/Roles/JobRequirement/AgeRequirement.cs b/Content.Shared/Roles/JobRequirement/AgeRequirement.cs new file mode 100644 index 000000000000..a8c210f2824f --- /dev/null +++ b/Content.Shared/Roles/JobRequirement/AgeRequirement.cs @@ -0,0 +1,47 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Preferences; +using JetBrains.Annotations; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Content.Shared.Roles; + +[UsedImplicitly] +[Serializable, NetSerializable] +public sealed partial class AgeRequirement : JobRequirement +{ + [DataField] + public int RequiredAge; + + public override bool Check(IEntityManager entManager, + IPrototypeManager protoManager, + HumanoidCharacterProfile? profile, + IReadOnlyDictionary playTimes, + [NotNullWhen(false)] out FormattedMessage? reason) + { + reason = new FormattedMessage(); + + if (profile is null) + return false; + + if (!Inverted) + { + reason = FormattedMessage.FromMarkupPermissive(Loc.GetString("role-timer-age-to-young", + ("age", RequiredAge))); + + if (profile.Age >= RequiredAge){} + return false; + } + else + { + reason = FormattedMessage.FromMarkupPermissive(Loc.GetString("role-timer-age-to-old", + ("age", RequiredAge))); + + if (profile.Age <= RequiredAge) + return false; + } + + return true; + } +} diff --git a/Content.Shared/Roles/JobRequirement/DepartmentTimeRequirement.cs b/Content.Shared/Roles/JobRequirement/DepartmentTimeRequirement.cs index f62a8debaf1d..d57c8681bc19 100644 --- a/Content.Shared/Roles/JobRequirement/DepartmentTimeRequirement.cs +++ b/Content.Shared/Roles/JobRequirement/DepartmentTimeRequirement.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using Content.Shared.Preferences; using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; @@ -24,6 +25,7 @@ public sealed partial class DepartmentTimeRequirement : JobRequirement public override bool Check(IEntityManager entManager, IPrototypeManager protoManager, + HumanoidCharacterProfile? profile, IReadOnlyDictionary playTimes, [NotNullWhen(false)] out FormattedMessage? reason) { diff --git a/Content.Shared/Roles/JobRequirement/OverallPlaytimeRequirement.cs b/Content.Shared/Roles/JobRequirement/OverallPlaytimeRequirement.cs index 88e515663e3a..709415d4261b 100644 --- a/Content.Shared/Roles/JobRequirement/OverallPlaytimeRequirement.cs +++ b/Content.Shared/Roles/JobRequirement/OverallPlaytimeRequirement.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared.Players.PlayTimeTracking; +using Content.Shared.Preferences; using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; @@ -17,6 +18,7 @@ public sealed partial class OverallPlaytimeRequirement : JobRequirement public override bool Check(IEntityManager entManager, IPrototypeManager protoManager, + HumanoidCharacterProfile? profile, IReadOnlyDictionary playTimes, [NotNullWhen(false)] out FormattedMessage? reason) { diff --git a/Content.Shared/Roles/JobRequirement/RoleTimeRequirement.cs b/Content.Shared/Roles/JobRequirement/RoleTimeRequirement.cs index 12d3142328a4..3c888d808b80 100644 --- a/Content.Shared/Roles/JobRequirement/RoleTimeRequirement.cs +++ b/Content.Shared/Roles/JobRequirement/RoleTimeRequirement.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared.Players.PlayTimeTracking; +using Content.Shared.Preferences; using Content.Shared.Roles.Jobs; using JetBrains.Annotations; using Robust.Shared.Prototypes; @@ -24,6 +25,7 @@ public sealed partial class RoleTimeRequirement : JobRequirement public override bool Check(IEntityManager entManager, IPrototypeManager protoManager, + HumanoidCharacterProfile? profile, IReadOnlyDictionary playTimes, [NotNullWhen(false)] out FormattedMessage? reason) { diff --git a/Content.Shared/Roles/JobRequirements.cs b/Content.Shared/Roles/JobRequirements.cs index cb09dc9396f1..17f5f7bd6a29 100644 --- a/Content.Shared/Roles/JobRequirements.cs +++ b/Content.Shared/Roles/JobRequirements.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using Content.Shared.Preferences; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -12,7 +13,8 @@ public static bool TryRequirementsMet( IReadOnlyDictionary playTimes, [NotNullWhen(false)] out FormattedMessage? reason, IEntityManager entManager, - IPrototypeManager protoManager) + IPrototypeManager protoManager, + HumanoidCharacterProfile? profile) { var sys = entManager.System(); var requirements = sys.GetJobRequirement(job); @@ -22,7 +24,7 @@ public static bool TryRequirementsMet( foreach (var requirement in requirements) { - if (!requirement.Check(entManager, protoManager, playTimes, out reason)) + if (!requirement.Check(entManager, protoManager, profile, playTimes, out reason)) return false; } @@ -43,6 +45,7 @@ public abstract partial class JobRequirement public abstract bool Check( IEntityManager entManager, IPrototypeManager protoManager, + HumanoidCharacterProfile? profile, IReadOnlyDictionary playTimes, [NotNullWhen(false)] out FormattedMessage? reason); } diff --git a/Resources/Locale/en-US/job/role-timers.ftl b/Resources/Locale/en-US/job/role-requirements.ftl similarity index 84% rename from Resources/Locale/en-US/job/role-timers.ftl rename to Resources/Locale/en-US/job/role-requirements.ftl index 1981f5e79577..38282f72d319 100644 --- a/Resources/Locale/en-US/job/role-timers.ftl +++ b/Resources/Locale/en-US/job/role-requirements.ftl @@ -4,6 +4,8 @@ role-timer-overall-insufficient = You require [color=yellow]{TOSTRING($time, "0" role-timer-overall-too-high = You require [color=yellow]{TOSTRING($time, "0")}[/color] fewer minutes of playtime to play this role. (Are you trying to play a trainee role?) role-timer-role-insufficient = You require [color=yellow]{TOSTRING($time, "0")}[/color] more minutes with [color={$departmentColor}]{$job}[/color] to play this role. role-timer-role-too-high = You require[color=yellow] {TOSTRING($time, "0")}[/color] fewer minutes with [color={$departmentColor}]{$job}[/color] to play this role. (Are you trying to play a trainee role?) +role-timer-age-to-old = Your character must be under the age of [color=yellow]{$age}[/color] to play this role. +role-timer-age-to-young = Your character must be over the age of [color=yellow]{$age}[/color] to play this role. role-timer-locked = Locked (hover for details) diff --git a/Resources/Prototypes/Roles/Jobs/Command/captain.yml b/Resources/Prototypes/Roles/Jobs/Command/captain.yml index 79634aa5d9f7..e2687dd65381 100644 --- a/Resources/Prototypes/Roles/Jobs/Command/captain.yml +++ b/Resources/Prototypes/Roles/Jobs/Command/captain.yml @@ -16,6 +16,8 @@ - !type:DepartmentTimeRequirement department: Command time: 54000 # 15 hours + - !type:AgeRequirement + requiredAge: 20 weight: 20 startingGear: CaptainGear icon: "JobIconCaptain" diff --git a/Resources/Prototypes/Roles/Jobs/Command/head_of_personnel.yml b/Resources/Prototypes/Roles/Jobs/Command/head_of_personnel.yml index d5521f767f18..c1b285303d2c 100644 --- a/Resources/Prototypes/Roles/Jobs/Command/head_of_personnel.yml +++ b/Resources/Prototypes/Roles/Jobs/Command/head_of_personnel.yml @@ -16,6 +16,8 @@ - !type:DepartmentTimeRequirement department: Command time: 36000 # 10 hours + - !type:AgeRequirement + requiredAge: 20 weight: 20 startingGear: HoPGear icon: "JobIconHeadOfPersonnel" diff --git a/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml b/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml index 0ee0b6736ca7..c0d9569c5da7 100644 --- a/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml +++ b/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml @@ -15,6 +15,8 @@ time: 36000 #10 hrs - !type:OverallPlaytimeRequirement time: 144000 #40 hrs + - !type:AgeRequirement + requiredAge: 20 weight: 10 startingGear: ChiefEngineerGear icon: "JobIconChiefEngineer" diff --git a/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml b/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml index 25871134155e..94248450cdad 100644 --- a/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml +++ b/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml @@ -17,6 +17,8 @@ time: 36000 #10 hrs - !type:OverallPlaytimeRequirement time: 144000 #40 hrs + - !type:AgeRequirement + requiredAge: 20 weight: 10 startingGear: CMOGear icon: "JobIconChiefMedicalOfficer" diff --git a/Resources/Prototypes/Roles/Jobs/Science/research_director.yml b/Resources/Prototypes/Roles/Jobs/Science/research_director.yml index b54ba54b1a47..b66e89f42077 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/research_director.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/research_director.yml @@ -9,6 +9,8 @@ time: 36000 #10 hrs - !type:OverallPlaytimeRequirement time: 144000 #40 hrs + - !type:AgeRequirement + requiredAge: 20 weight: 10 startingGear: ResearchDirectorGear icon: "JobIconResearchDirector" diff --git a/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml b/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml index 044df7f69e20..c02f1cee7605 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml @@ -15,6 +15,8 @@ time: 108000 # 30 hrs - !type:OverallPlaytimeRequirement time: 144000 #40 hrs + - !type:AgeRequirement + requiredAge: 20 weight: 10 startingGear: HoSGear icon: "JobIconHeadOfSecurity" From 7591a30acd4cc9976f3034ea667600f96983b9fd Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Thu, 25 Jul 2024 13:11:24 +0300 Subject: [PATCH 03/10] fix --- Content.Shared/Roles/JobRequirement/AgeRequirement.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Roles/JobRequirement/AgeRequirement.cs b/Content.Shared/Roles/JobRequirement/AgeRequirement.cs index a8c210f2824f..58224eb0a511 100644 --- a/Content.Shared/Roles/JobRequirement/AgeRequirement.cs +++ b/Content.Shared/Roles/JobRequirement/AgeRequirement.cs @@ -30,7 +30,7 @@ public override bool Check(IEntityManager entManager, reason = FormattedMessage.FromMarkupPermissive(Loc.GetString("role-timer-age-to-young", ("age", RequiredAge))); - if (profile.Age >= RequiredAge){} + if (profile.Age <= RequiredAge) return false; } else @@ -38,7 +38,7 @@ public override bool Check(IEntityManager entManager, reason = FormattedMessage.FromMarkupPermissive(Loc.GetString("role-timer-age-to-old", ("age", RequiredAge))); - if (profile.Age <= RequiredAge) + if (profile.Age >= RequiredAge) return false; } From 223ee4efe70817f9e5a686277366412b0cfceddc Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Thu, 25 Jul 2024 13:20:36 +0300 Subject: [PATCH 04/10] Update quartermaster.yml --- Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml b/Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml index 515e2f8425a7..0197cd0daf5a 100644 --- a/Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml +++ b/Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml @@ -15,6 +15,8 @@ time: 36000 #10 hours - !type:OverallPlaytimeRequirement time: 144000 #40 hrs + - !type:AgeRequirement + requiredAge: 20 weight: 10 startingGear: QuartermasterGear icon: "JobIconQuarterMaster" From 8926ce04ed97d67f7893f1148431dd1b6a56c405 Mon Sep 17 00:00:00 2001 From: Ed Date: Mon, 29 Jul 2024 14:12:15 +0300 Subject: [PATCH 05/10] sloth fixes --- .../PlayTimeTracking/JobRequirementsManager.cs | 4 +--- .../PlayTimeTracking/PlayTimeTrackingSystem.cs | 11 +++-------- Content.Shared/Roles/JobRequirement/AgeRequirement.cs | 5 ++++- .../Roles/JobRequirement/DepartmentTimeRequirement.cs | 4 ++-- .../JobRequirement/OverallPlaytimeRequirement.cs | 2 +- .../Roles/JobRequirement/RoleTimeRequirement.cs | 4 ++-- 6 files changed, 13 insertions(+), 17 deletions(-) diff --git a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs index 4c8d30391c73..71fc138e5f10 100644 --- a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs +++ b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs @@ -125,12 +125,10 @@ public bool CheckRoleRequirements(HashSet? requirements, [NotNul if (requirements == null || !_cfg.GetCVar(CCVars.GameRoleTimers)) return true; - var profile = (HumanoidCharacterProfile?) _preferencesManager.Preferences?.SelectedCharacter; - var reasons = new List(); foreach (var requirement in requirements) { - if (requirement.Check(_entManager, _prototypes, profile, _roles, out var jobReason)) + if (requirement.Check(_entManager, _prototypes, (HumanoidCharacterProfile?)_preferencesManager.Preferences?.SelectedCharacter, _roles, out var jobReason)) continue; reasons.Add(jobReason.ToMarkup()); diff --git a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs index bfbe1f5724f3..4139499e9fbe 100644 --- a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs +++ b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs @@ -208,9 +208,8 @@ public bool IsAllowed(ICommonSession player, string role) Log.Error($"Unable to check playtimes {Environment.StackTrace}"); playTimes = new Dictionary(); } - var profile = (HumanoidCharacterProfile?) _preferencesManager.GetPreferences(player.UserId).SelectedCharacter; - return JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes, profile); + return JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes, (HumanoidCharacterProfile?) _preferencesManager.GetPreferences(player.UserId).SelectedCharacter); } public HashSet> GetDisallowedJobs(ICommonSession player) @@ -225,11 +224,9 @@ public HashSet> GetDisallowedJobs(ICommonSession player) playTimes = new Dictionary(); } - var profile = (HumanoidCharacterProfile?) _preferencesManager.GetPreferences(player.UserId).SelectedCharacter; - foreach (var job in _prototypes.EnumeratePrototypes()) { - if (JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes, profile)) + if (JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes, (HumanoidCharacterProfile?) _preferencesManager.GetPreferences(player.UserId).SelectedCharacter)) roles.Add(job.ID); } @@ -249,12 +246,10 @@ public void RemoveDisallowedJobs(NetUserId userId, List> j playTimes ??= new Dictionary(); } - var profile = (HumanoidCharacterProfile?) _preferencesManager.GetPreferences(userId).SelectedCharacter; - for (var i = 0; i < jobs.Count; i++) { if (_prototypes.TryIndex(jobs[i], out var job) - && JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes, profile)) + && JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes, (HumanoidCharacterProfile?) _preferencesManager.GetPreferences(userId).SelectedCharacter)) { continue; } diff --git a/Content.Shared/Roles/JobRequirement/AgeRequirement.cs b/Content.Shared/Roles/JobRequirement/AgeRequirement.cs index 58224eb0a511..41d99126f35d 100644 --- a/Content.Shared/Roles/JobRequirement/AgeRequirement.cs +++ b/Content.Shared/Roles/JobRequirement/AgeRequirement.cs @@ -7,11 +7,14 @@ namespace Content.Shared.Roles; +/// +/// Requires the character to be older or younger than a certain age (inclusive) +/// [UsedImplicitly] [Serializable, NetSerializable] public sealed partial class AgeRequirement : JobRequirement { - [DataField] + [DataField(required: true)] public int RequiredAge; public override bool Check(IEntityManager entManager, diff --git a/Content.Shared/Roles/JobRequirement/DepartmentTimeRequirement.cs b/Content.Shared/Roles/JobRequirement/DepartmentTimeRequirement.cs index d57c8681bc19..0f48369de580 100644 --- a/Content.Shared/Roles/JobRequirement/DepartmentTimeRequirement.cs +++ b/Content.Shared/Roles/JobRequirement/DepartmentTimeRequirement.cs @@ -14,13 +14,13 @@ public sealed partial class DepartmentTimeRequirement : JobRequirement /// /// Which department needs the required amount of time. /// - [DataField] + [DataField(required: true)] public ProtoId Department = default!; /// /// How long (in seconds) this requirement is. /// - [DataField] + [DataField(required: true)] public TimeSpan Time; public override bool Check(IEntityManager entManager, diff --git a/Content.Shared/Roles/JobRequirement/OverallPlaytimeRequirement.cs b/Content.Shared/Roles/JobRequirement/OverallPlaytimeRequirement.cs index 709415d4261b..acbb8f2b4d47 100644 --- a/Content.Shared/Roles/JobRequirement/OverallPlaytimeRequirement.cs +++ b/Content.Shared/Roles/JobRequirement/OverallPlaytimeRequirement.cs @@ -13,7 +13,7 @@ namespace Content.Shared.Roles; public sealed partial class OverallPlaytimeRequirement : JobRequirement { /// - [DataField] + [DataField(required: true)] public TimeSpan Time; public override bool Check(IEntityManager entManager, diff --git a/Content.Shared/Roles/JobRequirement/RoleTimeRequirement.cs b/Content.Shared/Roles/JobRequirement/RoleTimeRequirement.cs index 3c888d808b80..658db95ab566 100644 --- a/Content.Shared/Roles/JobRequirement/RoleTimeRequirement.cs +++ b/Content.Shared/Roles/JobRequirement/RoleTimeRequirement.cs @@ -16,11 +16,11 @@ public sealed partial class RoleTimeRequirement : JobRequirement /// /// What particular role they need the time requirement with. /// - [DataField] + [DataField(required: true)] public ProtoId Role = default!; /// - [DataField] + [DataField(required: true)] public TimeSpan Time; public override bool Check(IEntityManager entManager, From 5e3d1d6e5d2297fa8290de2f41af1e2a2d0a32e4 Mon Sep 17 00:00:00 2001 From: Ed Date: Mon, 29 Jul 2024 14:25:52 +0300 Subject: [PATCH 06/10] inport 30208 --- Content.Shared/Roles/DepartmentPrototype.cs | 8 +++++++- .../Roles/JobRequirement/DepartmentTimeRequirement.cs | 10 ++++++++-- Resources/Locale/en-US/job/role-requirements.ftl | 2 ++ Resources/Prototypes/Roles/Jobs/departments.yml | 8 ++++++++ 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Content.Shared/Roles/DepartmentPrototype.cs b/Content.Shared/Roles/DepartmentPrototype.cs index d6288bec9098..4c78fa60137a 100644 --- a/Content.Shared/Roles/DepartmentPrototype.cs +++ b/Content.Shared/Roles/DepartmentPrototype.cs @@ -9,7 +9,13 @@ public sealed partial class DepartmentPrototype : IPrototype public string ID { get; } = string.Empty; /// - /// A description string to display in the character menu as an explanation of the department's function. + /// The name LocId of the department that will be displayed in the various menus. + /// + [DataField(required: true)] + public LocId Name = string.Empty; + + /// + /// A description LocId to display in the character menu as an explanation of the department's function. /// [DataField(required: true)] public string Description = string.Empty; diff --git a/Content.Shared/Roles/JobRequirement/DepartmentTimeRequirement.cs b/Content.Shared/Roles/JobRequirement/DepartmentTimeRequirement.cs index 0f48369de580..56b7d8ba811b 100644 --- a/Content.Shared/Roles/JobRequirement/DepartmentTimeRequirement.cs +++ b/Content.Shared/Roles/JobRequirement/DepartmentTimeRequirement.cs @@ -48,6 +48,12 @@ public override bool Check(IEntityManager entManager, } var deptDiff = Time.TotalMinutes - playtime.TotalMinutes; + var nameDepartment = "role-timer-department-unknown"; + + if (protoManager.TryIndex(Department, out var departmentIndexed)) + { + nameDepartment = departmentIndexed.Name; + } if (!Inverted) { @@ -57,7 +63,7 @@ public override bool Check(IEntityManager entManager, reason = FormattedMessage.FromMarkupPermissive(Loc.GetString( "role-timer-department-insufficient", ("time", Math.Ceiling(deptDiff)), - ("department", Loc.GetString(Department)), + ("department", Loc.GetString(nameDepartment)), ("departmentColor", department.Color.ToHex()))); return false; } @@ -67,7 +73,7 @@ public override bool Check(IEntityManager entManager, reason = FormattedMessage.FromMarkupPermissive(Loc.GetString( "role-timer-department-too-high", ("time", -deptDiff), - ("department", Loc.GetString(Department)), + ("department", Loc.GetString(nameDepartment)), ("departmentColor", department.Color.ToHex()))); return false; } diff --git a/Resources/Locale/en-US/job/role-requirements.ftl b/Resources/Locale/en-US/job/role-requirements.ftl index 38282f72d319..882f92d6b28f 100644 --- a/Resources/Locale/en-US/job/role-requirements.ftl +++ b/Resources/Locale/en-US/job/role-requirements.ftl @@ -9,4 +9,6 @@ role-timer-age-to-young = Your character must be over the age of [color=yellow]{ role-timer-locked = Locked (hover for details) +role-timer-department-unknown = Unknown Department + role-ban = You have been banned from this role. diff --git a/Resources/Prototypes/Roles/Jobs/departments.yml b/Resources/Prototypes/Roles/Jobs/departments.yml index 9be98be950c6..6d25d8fd881c 100644 --- a/Resources/Prototypes/Roles/Jobs/departments.yml +++ b/Resources/Prototypes/Roles/Jobs/departments.yml @@ -1,5 +1,6 @@ - type: department id: Cargo + name: department-Cargo description: department-Cargo-description color: "#A46106" roles: @@ -9,6 +10,7 @@ - type: department id: Civilian + name: department-Civilian description: department-Civilian-description color: "#9FED58" weight: -10 @@ -34,6 +36,7 @@ - type: department id: Command + name: department-Command description: department-Command-description color: "#334E6D" roles: @@ -50,6 +53,7 @@ - type: department id: Engineering + name: department-Engineering description: department-Engineering-description color: "#EFB341" roles: @@ -60,6 +64,7 @@ - type: department id: Medical + name: department-Medical description: department-Medical-description color: "#52B4E9" roles: @@ -72,6 +77,7 @@ - type: department id: Security + name: department-Security description: department-Security-description color: "#DE3A3A" weight: 20 @@ -84,6 +90,7 @@ - type: department id: Science + name: department-Science description: department-Science-description color: "#D381C9" roles: @@ -93,6 +100,7 @@ - type: department id: Specific + name: department-Specific description: department-Specific-description color: "#9FED58" weight: 10 From 55b0e48381e5d27aa5bb42cba2ccda4baad8c2fe Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Mon, 29 Jul 2024 20:15:13 +0300 Subject: [PATCH 07/10] Update DepartmentPrototype.cs --- Content.Shared/Roles/DepartmentPrototype.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Roles/DepartmentPrototype.cs b/Content.Shared/Roles/DepartmentPrototype.cs index 4c78fa60137a..5c94c90f8b34 100644 --- a/Content.Shared/Roles/DepartmentPrototype.cs +++ b/Content.Shared/Roles/DepartmentPrototype.cs @@ -18,7 +18,7 @@ public sealed partial class DepartmentPrototype : IPrototype /// A description LocId to display in the character menu as an explanation of the department's function. /// [DataField(required: true)] - public string Description = string.Empty; + public LocId Description = string.Empty; /// /// A color representing this department to use for text. From 495eca273a5ff00c505244dac266978722c8c9cf Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 30 Jul 2024 00:39:43 +0300 Subject: [PATCH 08/10] species restriction --- .../JobRequirement/SpeciesRequirement.cs | 56 +++++++++++++++++++ .../Locale/en-US/job/role-requirements.ftl | 2 + 2 files changed, 58 insertions(+) create mode 100644 Content.Shared/Roles/JobRequirement/SpeciesRequirement.cs diff --git a/Content.Shared/Roles/JobRequirement/SpeciesRequirement.cs b/Content.Shared/Roles/JobRequirement/SpeciesRequirement.cs new file mode 100644 index 000000000000..577a7426a0b1 --- /dev/null +++ b/Content.Shared/Roles/JobRequirement/SpeciesRequirement.cs @@ -0,0 +1,56 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Humanoid.Prototypes; +using Content.Shared.Preferences; +using JetBrains.Annotations; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Content.Shared.Roles; + +/// +/// Requires the character to be or not be on the list of specified species +/// +[UsedImplicitly] +[Serializable, NetSerializable] +public sealed partial class SpeciesRequirement : JobRequirement +{ + [DataField(required: true)] + public HashSet> Species = new(); + + public override bool Check(IEntityManager entManager, + IPrototypeManager protoManager, + HumanoidCharacterProfile? profile, + IReadOnlyDictionary playTimes, + [NotNullWhen(false)] out FormattedMessage? reason) + { + reason = new FormattedMessage(); + + if (profile is null) + return false; + + var speciesList = "[color=yellow]"; + foreach (var s in Species) + { + speciesList += Loc.GetString(protoManager.Index(s).Name) + " "; + } + speciesList += "[/color]"; + + if (!Inverted) + { + reason = FormattedMessage.FromMarkupPermissive($"{Loc.GetString("role-timer-whitelisted-species")}\n{speciesList}"); + + if (!Species.Contains(profile.Species)) + return false; + } + else + { + reason = FormattedMessage.FromMarkupPermissive($"{Loc.GetString("role-timer-blacklisted-species")}\n{speciesList}"); + + if (Species.Contains(profile.Species)) + return false; + } + + return true; + } +} diff --git a/Resources/Locale/en-US/job/role-requirements.ftl b/Resources/Locale/en-US/job/role-requirements.ftl index 882f92d6b28f..906a0470af0d 100644 --- a/Resources/Locale/en-US/job/role-requirements.ftl +++ b/Resources/Locale/en-US/job/role-requirements.ftl @@ -6,6 +6,8 @@ role-timer-role-insufficient = You require [color=yellow]{TOSTRING($time, "0")}[ role-timer-role-too-high = You require[color=yellow] {TOSTRING($time, "0")}[/color] fewer minutes with [color={$departmentColor}]{$job}[/color] to play this role. (Are you trying to play a trainee role?) role-timer-age-to-old = Your character must be under the age of [color=yellow]{$age}[/color] to play this role. role-timer-age-to-young = Your character must be over the age of [color=yellow]{$age}[/color] to play this role. +role-timer-whitelisted-species = Your character must be one of the following species to play this role: +role-timer-blacklisted-species = Your character must not be one of the following species to play this role: role-timer-locked = Locked (hover for details) From a5bddc648ce86a92062f06c544e41bf57fa6949f Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:49:18 +0300 Subject: [PATCH 09/10] left tweak stick --- Content.Client/LateJoin/LateJoinGui.cs | 5 ++++- .../Lobby/UI/HumanoidProfileEditor.xaml.cs | 4 ++-- .../PlayTimeTracking/JobRequirementsManager.cs | 13 ++++++------- .../Systems/Ghost/Controls/Roles/GhostRolesEui.cs | 2 +- .../Roles/JobRequirement/AgeRequirement.cs | 4 ++-- .../Roles/JobRequirement/SpeciesRequirement.cs | 4 ++-- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Content.Client/LateJoin/LateJoinGui.cs b/Content.Client/LateJoin/LateJoinGui.cs index 62a06629f294..13cf281513d2 100644 --- a/Content.Client/LateJoin/LateJoinGui.cs +++ b/Content.Client/LateJoin/LateJoinGui.cs @@ -2,9 +2,11 @@ using System.Numerics; using Content.Client.CrewManifest; using Content.Client.GameTicking.Managers; +using Content.Client.Lobby; using Content.Client.UserInterface.Controls; using Content.Client.Players.PlayTimeTracking; using Content.Shared.CCVar; +using Content.Shared.Preferences; using Content.Shared.Roles; using Content.Shared.StatusIcon; using Robust.Client.Console; @@ -26,6 +28,7 @@ public sealed class LateJoinGui : DefaultWindow [Dependency] private readonly IConfigurationManager _configManager = default!; [Dependency] private readonly IEntitySystemManager _entitySystem = default!; [Dependency] private readonly JobRequirementsManager _jobRequirements = default!; + [Dependency] private readonly IClientPreferencesManager _preferencesManager = default!; public event Action<(NetEntity, string)> SelectedId; @@ -254,7 +257,7 @@ private void RebuildUI() jobButton.OnPressed += _ => SelectedId.Invoke((id, jobButton.JobId)); - if (!_jobRequirements.IsAllowed(prototype, out var reason)) + if (!_jobRequirements.IsAllowed(prototype, (HumanoidCharacterProfile?)_preferencesManager.Preferences?.SelectedCharacter, out var reason)) { jobButton.Disabled = true; diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs index 1a3bd508ca0d..3c58942ce84e 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs @@ -634,7 +634,7 @@ public void RefreshAntags() selector.Select(Profile?.AntagPreferences.Contains(antag.ID) == true ? 0 : 1); var requirements = _entManager.System().GetAntagRequirement(antag); - if (!_requirements.CheckRoleRequirements(requirements, out var reason)) + if (!_requirements.CheckRoleRequirements(requirements, (HumanoidCharacterProfile?)_preferencesManager.Preferences?.SelectedCharacter, out var reason)) { selector.LockRequirements(reason); Profile = Profile?.WithAntagPreference(antag.ID, false); @@ -887,7 +887,7 @@ public void RefreshJobs() icon.Texture = jobIcon.Icon.Frame0(); selector.Setup(items, job.LocalizedName, 200, job.LocalizedDescription, icon, job.Guides); - if (!_requirements.IsAllowed(job, out var reason)) + if (!_requirements.IsAllowed(job, (HumanoidCharacterProfile?)_preferencesManager.Preferences?.SelectedCharacter, out var reason)) { selector.LockRequirements(reason); } diff --git a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs index 71fc138e5f10..8ce22489c718 100644 --- a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs +++ b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs @@ -24,7 +24,6 @@ public sealed class JobRequirementsManager : ISharedPlaytimeManager [Dependency] private readonly IEntityManager _entManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPrototypeManager _prototypes = default!; - [Dependency] private readonly IClientPreferencesManager _preferencesManager = default!; private readonly Dictionary _roles = new(); private readonly List _roleBans = new(); @@ -92,7 +91,7 @@ private void RxJobWhitelist(MsgJobWhitelist message) Updated?.Invoke(); } - public bool IsAllowed(JobPrototype job, [NotNullWhen(false)] out FormattedMessage? reason) + public bool IsAllowed(JobPrototype job, HumanoidCharacterProfile? profile, [NotNullWhen(false)] out FormattedMessage? reason) { reason = null; @@ -109,16 +108,16 @@ public bool IsAllowed(JobPrototype job, [NotNullWhen(false)] out FormattedMessag if (player == null) return true; - return CheckRoleRequirements(job, out reason); + return CheckRoleRequirements(job, profile, out reason); } - public bool CheckRoleRequirements(JobPrototype job, [NotNullWhen(false)] out FormattedMessage? reason) + public bool CheckRoleRequirements(JobPrototype job, HumanoidCharacterProfile? profile, [NotNullWhen(false)] out FormattedMessage? reason) { var reqs = _entManager.System().GetJobRequirement(job); - return CheckRoleRequirements(reqs, out reason); + return CheckRoleRequirements(reqs, profile, out reason); } - public bool CheckRoleRequirements(HashSet? requirements, [NotNullWhen(false)] out FormattedMessage? reason) + public bool CheckRoleRequirements(HashSet? requirements, HumanoidCharacterProfile? profile, [NotNullWhen(false)] out FormattedMessage? reason) { reason = null; @@ -128,7 +127,7 @@ public bool CheckRoleRequirements(HashSet? requirements, [NotNul var reasons = new List(); foreach (var requirement in requirements) { - if (requirement.Check(_entManager, _prototypes, (HumanoidCharacterProfile?)_preferencesManager.Preferences?.SelectedCharacter, _roles, out var jobReason)) + if (requirement.Check(_entManager, _prototypes, profile, _roles, out var jobReason)) continue; reasons.Add(jobReason.ToMarkup()); diff --git a/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesEui.cs b/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesEui.cs index 8453d65cf6f4..6b183362e56e 100644 --- a/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesEui.cs +++ b/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesEui.cs @@ -93,7 +93,7 @@ public override void HandleState(EuiStateBase state) bool hasAccess = true; FormattedMessage? reason; - if (!requirementsManager.CheckRoleRequirements(group.Key.Requirements, out reason)) + if (!requirementsManager.CheckRoleRequirements(group.Key.Requirements, null, out reason)) { hasAccess = false; } diff --git a/Content.Shared/Roles/JobRequirement/AgeRequirement.cs b/Content.Shared/Roles/JobRequirement/AgeRequirement.cs index 41d99126f35d..2cdc8e001c11 100644 --- a/Content.Shared/Roles/JobRequirement/AgeRequirement.cs +++ b/Content.Shared/Roles/JobRequirement/AgeRequirement.cs @@ -25,8 +25,8 @@ public override bool Check(IEntityManager entManager, { reason = new FormattedMessage(); - if (profile is null) - return false; + if (profile is null) //the profile could be null if the player is a ghost. In this case we don't need to block the role selection for ghostrole + return true; if (!Inverted) { diff --git a/Content.Shared/Roles/JobRequirement/SpeciesRequirement.cs b/Content.Shared/Roles/JobRequirement/SpeciesRequirement.cs index 577a7426a0b1..ef209db88fc3 100644 --- a/Content.Shared/Roles/JobRequirement/SpeciesRequirement.cs +++ b/Content.Shared/Roles/JobRequirement/SpeciesRequirement.cs @@ -26,8 +26,8 @@ public override bool Check(IEntityManager entManager, { reason = new FormattedMessage(); - if (profile is null) - return false; + if (profile is null) //the profile could be null if the player is a ghost. In this case we don't need to block the role selection for ghostrole + return true; var speciesList = "[color=yellow]"; foreach (var s in Species) From 2a3d2b5db5bbc8e504c26dc68b23953dd2fa2b2a Mon Sep 17 00:00:00 2001 From: Ed Date: Sun, 4 Aug 2024 23:55:11 +0300 Subject: [PATCH 10/10] stringbuilder is cool! --- .../Roles/JobRequirement/SpeciesRequirement.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Content.Shared/Roles/JobRequirement/SpeciesRequirement.cs b/Content.Shared/Roles/JobRequirement/SpeciesRequirement.cs index ef209db88fc3..68c069931f37 100644 --- a/Content.Shared/Roles/JobRequirement/SpeciesRequirement.cs +++ b/Content.Shared/Roles/JobRequirement/SpeciesRequirement.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using System.Text; using Content.Shared.Humanoid.Prototypes; using Content.Shared.Preferences; using JetBrains.Annotations; @@ -29,23 +30,25 @@ public override bool Check(IEntityManager entManager, if (profile is null) //the profile could be null if the player is a ghost. In this case we don't need to block the role selection for ghostrole return true; - var speciesList = "[color=yellow]"; + var sb = new StringBuilder(); + sb.Append("[color=yellow]"); foreach (var s in Species) { - speciesList += Loc.GetString(protoManager.Index(s).Name) + " "; + sb.Append(Loc.GetString(protoManager.Index(s).Name) + " "); } - speciesList += "[/color]"; + + sb.Append("[/color]"); if (!Inverted) { - reason = FormattedMessage.FromMarkupPermissive($"{Loc.GetString("role-timer-whitelisted-species")}\n{speciesList}"); + reason = FormattedMessage.FromMarkupPermissive($"{Loc.GetString("role-timer-whitelisted-species")}\n{sb}"); if (!Species.Contains(profile.Species)) return false; } else { - reason = FormattedMessage.FromMarkupPermissive($"{Loc.GetString("role-timer-blacklisted-species")}\n{speciesList}"); + reason = FormattedMessage.FromMarkupPermissive($"{Loc.GetString("role-timer-blacklisted-species")}\n{sb}"); if (Species.Contains(profile.Species)) return false;