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;
[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;
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;
selector.Select(Profile?.AntagPreferences.Contains(antag.ID) == true ? 0 : 1);
var requirements = _entManager.System<SharedRoleSystem>().GetAntagRequirement(antag);
- if (!_requirements.CheckRoleTime(requirements, out var reason))
+ if (!_requirements.CheckRoleRequirements(requirements, (HumanoidCharacterProfile?)_preferencesManager.Preferences?.SelectedCharacter, out var reason))
{
selector.LockRequirements(reason);
Profile = Profile?.WithAntagPreference(antag.ID, false);
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);
}
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;
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;
if (player == null)
return true;
- return CheckRoleTime(job, out reason);
+ return CheckRoleRequirements(job, profile, out reason);
}
- public bool CheckRoleTime(JobPrototype job, [NotNullWhen(false)] out FormattedMessage? reason)
+ public bool CheckRoleRequirements(JobPrototype job, HumanoidCharacterProfile? profile, [NotNullWhen(false)] out FormattedMessage? reason)
{
var reqs = _entManager.System<SharedRoleSystem>().GetJobRequirement(job);
- return CheckRoleTime(reqs, out reason);
+ return CheckRoleRequirements(reqs, profile, out reason);
}
- public bool CheckRoleTime(HashSet<JobRequirement>? requirements, [NotNullWhen(false)] out FormattedMessage? reason)
+ public bool CheckRoleRequirements(HashSet<JobRequirement>? requirements, HumanoidCharacterProfile? profile, [NotNullWhen(false)] out FormattedMessage? reason)
{
reason = null;
var reasons = new List<string>();
foreach (var requirement in requirements)
{
- if (JobRequirements.TryRequirementMet(requirement, _roles, out var jobReason, _entManager, _prototypes))
+ if (requirement.Check(_entManager, _prototypes, profile, _roles, out var jobReason))
continue;
reasons.Add(jobReason.ToMarkup());
bool hasAccess = true;
FormattedMessage? reason;
- if (!requirementsManager.CheckRoleTime(group.Key.Requirements, out reason))
+ if (!requirementsManager.CheckRoleRequirements(group.Key.Requirements, null, out reason))
{
hasAccess = false;
}
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;
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;
[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()
{
playTimes = new Dictionary<string, TimeSpan>();
}
- return JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes);
+ return JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes, (HumanoidCharacterProfile?) _preferencesManager.GetPreferences(player.UserId).SelectedCharacter);
}
public HashSet<ProtoId<JobPrototype>> GetDisallowedJobs(ICommonSession player)
foreach (var job in _prototypes.EnumeratePrototypes<JobPrototype>())
{
- if (JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes))
+ if (JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes, (HumanoidCharacterProfile?) _preferencesManager.GetPreferences(player.UserId).SelectedCharacter))
roles.Add(job.ID);
}
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, (HumanoidCharacterProfile?) _preferencesManager.GetPreferences(userId).SelectedCharacter))
{
continue;
}
var manager = collection.Resolve<ISharedPlaytimeManager>();
var playtimes = manager.GetPlayTimes(session);
- return JobRequirements.TryRequirementMet(Requirement, playtimes, out reason,
- collection.Resolve<IEntityManager>(),
- collection.Resolve<IPrototypeManager>());
+ return Requirement.Check(collection.Resolve<IEntityManager>(),
+ collection.Resolve<IPrototypeManager>(),
+ profile,
+ playtimes,
+ out reason);
}
}
public string ID { get; } = string.Empty;
/// <summary>
- /// 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.
/// </summary>
[DataField(required: true)]
- public string Description = string.Empty;
+ public LocId Name = string.Empty;
+
+ /// <summary>
+ /// A description LocId to display in the character menu as an explanation of the department's function.
+ /// </summary>
+ [DataField(required: true)]
+ public LocId Description = string.Empty;
/// <summary>
/// A color representing this department to use for text.
--- /dev/null
+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;
+
+/// <summary>
+/// Requires the character to be older or younger than a certain age (inclusive)
+/// </summary>
+[UsedImplicitly]
+[Serializable, NetSerializable]
+public sealed partial class AgeRequirement : JobRequirement
+{
+ [DataField(required: true)]
+ public int RequiredAge;
+
+ public override bool Check(IEntityManager entManager,
+ IPrototypeManager protoManager,
+ HumanoidCharacterProfile? profile,
+ IReadOnlyDictionary<string, TimeSpan> playTimes,
+ [NotNullWhen(false)] out FormattedMessage? reason)
+ {
+ reason = new FormattedMessage();
+
+ 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)
+ {
+ 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;
+ }
+}
--- /dev/null
+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 DepartmentTimeRequirement : JobRequirement
+{
+ /// <summary>
+ /// Which department needs the required amount of time.
+ /// </summary>
+ [DataField(required: true)]
+ public ProtoId<DepartmentPrototype> Department = default!;
+
+ /// <summary>
+ /// How long (in seconds) this requirement is.
+ /// </summary>
+ [DataField(required: true)]
+ public TimeSpan Time;
+
+ public override bool Check(IEntityManager entManager,
+ IPrototypeManager protoManager,
+ HumanoidCharacterProfile? profile,
+ IReadOnlyDictionary<string, TimeSpan> 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;
+ var nameDepartment = "role-timer-department-unknown";
+
+ if (protoManager.TryIndex(Department, out var departmentIndexed))
+ {
+ nameDepartment = departmentIndexed.Name;
+ }
+
+ if (!Inverted)
+ {
+ if (deptDiff <= 0)
+ return true;
+
+ reason = FormattedMessage.FromMarkupPermissive(Loc.GetString(
+ "role-timer-department-insufficient",
+ ("time", Math.Ceiling(deptDiff)),
+ ("department", Loc.GetString(nameDepartment)),
+ ("departmentColor", department.Color.ToHex())));
+ return false;
+ }
+
+ if (deptDiff <= 0)
+ {
+ reason = FormattedMessage.FromMarkupPermissive(Loc.GetString(
+ "role-timer-department-too-high",
+ ("time", -deptDiff),
+ ("department", Loc.GetString(nameDepartment)),
+ ("departmentColor", department.Color.ToHex())));
+ return false;
+ }
+
+ return true;
+ }
+}
--- /dev/null
+using System.Diagnostics.CodeAnalysis;
+using Content.Shared.Players.PlayTimeTracking;
+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 OverallPlaytimeRequirement : JobRequirement
+{
+ /// <inheritdoc cref="DepartmentTimeRequirement.Time"/>
+ [DataField(required: true)]
+ public TimeSpan Time;
+
+ public override bool Check(IEntityManager entManager,
+ IPrototypeManager protoManager,
+ HumanoidCharacterProfile? profile,
+ IReadOnlyDictionary<string, TimeSpan> 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;
+ }
+}
--- /dev/null
+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;
+using Robust.Shared.Serialization;
+using Robust.Shared.Utility;
+
+namespace Content.Shared.Roles;
+
+[UsedImplicitly]
+[Serializable, NetSerializable]
+public sealed partial class RoleTimeRequirement : JobRequirement
+{
+ /// <summary>
+ /// What particular role they need the time requirement with.
+ /// </summary>
+ [DataField(required: true)]
+ public ProtoId<PlayTimeTrackerPrototype> Role = default!;
+
+ /// <inheritdoc cref="DepartmentTimeRequirement.Time"/>
+ [DataField(required: true)]
+ public TimeSpan Time;
+
+ public override bool Check(IEntityManager entManager,
+ IPrototypeManager protoManager,
+ HumanoidCharacterProfile? profile,
+ IReadOnlyDictionary<string, TimeSpan> 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;
+ }
+}
--- /dev/null
+using System.Diagnostics.CodeAnalysis;
+using System.Text;
+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;
+
+/// <summary>
+/// Requires the character to be or not be on the list of specified species
+/// </summary>
+[UsedImplicitly]
+[Serializable, NetSerializable]
+public sealed partial class SpeciesRequirement : JobRequirement
+{
+ [DataField(required: true)]
+ public HashSet<ProtoId<SpeciesPrototype>> Species = new();
+
+ public override bool Check(IEntityManager entManager,
+ IPrototypeManager protoManager,
+ HumanoidCharacterProfile? profile,
+ IReadOnlyDictionary<string, TimeSpan> playTimes,
+ [NotNullWhen(false)] out FormattedMessage? reason)
+ {
+ reason = new FormattedMessage();
+
+ 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 sb = new StringBuilder();
+ sb.Append("[color=yellow]");
+ foreach (var s in Species)
+ {
+ sb.Append(Loc.GetString(protoManager.Index(s).Name) + " ");
+ }
+
+ sb.Append("[/color]");
+
+ if (!Inverted)
+ {
+ 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{sb}");
+
+ if (Species.Contains(profile.Species))
+ return false;
+ }
+
+ return true;
+ }
+}
using System.Diagnostics.CodeAnalysis;
-using Content.Shared.Players.PlayTimeTracking;
-using Content.Shared.Roles.Jobs;
-using JetBrains.Annotations;
+using Content.Shared.Preferences;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Utility;
-namespace Content.Shared.Roles
-{
- /// <summary>
- /// Abstract class for playtime and other requirements for role gates.
- /// </summary>
- [ImplicitDataDefinitionForInheritors]
- [Serializable, NetSerializable]
- public abstract partial class JobRequirement{}
-
- [UsedImplicitly]
- [Serializable, NetSerializable]
- public sealed partial class DepartmentTimeRequirement : JobRequirement
- {
- /// <summary>
- /// Which department needs the required amount of time.
- /// </summary>
- [DataField("department", customTypeSerializer: typeof(PrototypeIdSerializer<DepartmentPrototype>))]
- public string Department = default!;
-
- /// <summary>
- /// How long (in seconds) this requirement is.
- /// </summary>
- [DataField("time")] public TimeSpan Time;
-
- /// <summary>
- /// If true, requirement will return false if playtime above the specified time.
- /// </summary>
- /// <value>
- /// <c>False</c> by default.<br />
- /// <c>True</c> for invert general requirement
- /// </value>
- [DataField("inverted")] public bool Inverted;
- }
-
- [UsedImplicitly]
- [Serializable, NetSerializable]
- public sealed partial class RoleTimeRequirement : JobRequirement
- {
- /// <summary>
- /// What particular role they need the time requirement with.
- /// </summary>
- [DataField("role", customTypeSerializer: typeof(PrototypeIdSerializer<PlayTimeTrackerPrototype>))]
- public string Role = default!;
-
- /// <inheritdoc cref="DepartmentTimeRequirement.Time"/>
- [DataField("time")] public TimeSpan Time;
-
- /// <inheritdoc cref="DepartmentTimeRequirement.Inverted"/>
- [DataField("inverted")] public bool Inverted;
- }
+namespace Content.Shared.Roles;
- [UsedImplicitly]
- [Serializable, NetSerializable]
- public sealed partial class OverallPlaytimeRequirement : JobRequirement
- {
- /// <inheritdoc cref="DepartmentTimeRequirement.Time"/>
- [DataField("time")] public TimeSpan Time;
-
- /// <inheritdoc cref="DepartmentTimeRequirement.Inverted"/>
- [DataField("inverted")] public bool Inverted;
- }
-
- public static class JobRequirements
+public static class JobRequirements
+{
+ public static bool TryRequirementsMet(
+ JobPrototype job,
+ IReadOnlyDictionary<string, TimeSpan> playTimes,
+ [NotNullWhen(false)] out FormattedMessage? reason,
+ IEntityManager entManager,
+ IPrototypeManager protoManager,
+ HumanoidCharacterProfile? profile)
{
- public static bool TryRequirementsMet(
- JobPrototype job,
- IReadOnlyDictionary<string, TimeSpan> playTimes,
- [NotNullWhen(false)] out FormattedMessage? reason,
- IEntityManager entManager,
- IPrototypeManager prototypes)
- {
- var sys = entManager.System<SharedRoleSystem>();
- 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<SharedRoleSystem>();
+ var requirements = sys.GetJobRequirement(job);
+ reason = null;
+ if (requirements == null)
return true;
- }
- /// <summary>
- /// Returns a string with the reason why a particular requirement may not be met.
- /// </summary>
- public static bool TryRequirementMet(
- JobRequirement requirement,
- IReadOnlyDictionary<string, TimeSpan> 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<DepartmentPrototype>(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<JobPrototype>(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, profile, playTimes, out reason))
+ return false;
}
+
+ return true;
}
}
+
+/// <summary>
+/// Abstract class for playtime and other requirements for role gates.
+/// </summary>
+[ImplicitDataDefinitionForInheritors]
+[Serializable, NetSerializable]
+public abstract partial class JobRequirement
+{
+ [DataField]
+ public bool Inverted;
+
+ public abstract bool Check(
+ IEntityManager entManager,
+ IPrototypeManager protoManager,
+ HumanoidCharacterProfile? profile,
+ IReadOnlyDictionary<string, TimeSpan> playTimes,
+ [NotNullWhen(false)] out FormattedMessage? reason);
+}
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-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)
+role-timer-department-unknown = Unknown Department
+
role-ban = You have been banned from this role.
time: 36000 #10 hours
- !type:OverallPlaytimeRequirement
time: 144000 #40 hrs
+ - !type:AgeRequirement
+ requiredAge: 20
weight: 10
startingGear: QuartermasterGear
icon: "JobIconQuarterMaster"
- !type:DepartmentTimeRequirement
department: Command
time: 54000 # 15 hours
+ - !type:AgeRequirement
+ requiredAge: 20
weight: 20
startingGear: CaptainGear
icon: "JobIconCaptain"
- !type:DepartmentTimeRequirement
department: Command
time: 36000 # 10 hours
+ - !type:AgeRequirement
+ requiredAge: 20
weight: 20
startingGear: HoPGear
icon: "JobIconHeadOfPersonnel"
time: 36000 #10 hrs
- !type:OverallPlaytimeRequirement
time: 144000 #40 hrs
+ - !type:AgeRequirement
+ requiredAge: 20
weight: 10
startingGear: ChiefEngineerGear
icon: "JobIconChiefEngineer"
time: 36000 #10 hrs
- !type:OverallPlaytimeRequirement
time: 144000 #40 hrs
+ - !type:AgeRequirement
+ requiredAge: 20
weight: 10
startingGear: CMOGear
icon: "JobIconChiefMedicalOfficer"
time: 36000 #10 hrs
- !type:OverallPlaytimeRequirement
time: 144000 #40 hrs
+ - !type:AgeRequirement
+ requiredAge: 20
weight: 10
startingGear: ResearchDirectorGear
icon: "JobIconResearchDirector"
time: 108000 # 30 hrs
- !type:OverallPlaytimeRequirement
time: 144000 #40 hrs
+ - !type:AgeRequirement
+ requiredAge: 20
weight: 10
startingGear: HoSGear
icon: "JobIconHeadOfSecurity"
- type: department
id: Cargo
+ name: department-Cargo
description: department-Cargo-description
color: "#A46106"
roles:
- type: department
id: Civilian
+ name: department-Civilian
description: department-Civilian-description
color: "#9FED58"
weight: -10
- type: department
id: Command
+ name: department-Command
description: department-Command-description
color: "#334E6D"
roles:
- type: department
id: Engineering
+ name: department-Engineering
description: department-Engineering-description
color: "#EFB341"
roles:
- type: department
id: Medical
+ name: department-Medical
description: department-Medical-description
color: "#52B4E9"
roles:
- type: department
id: Security
+ name: department-Security
description: department-Security-description
color: "#DE3A3A"
weight: 20
- type: department
id: Science
+ name: department-Science
description: department-Science-description
color: "#D381C9"
roles:
- type: department
id: Specific
+ name: department-Specific
description: department-Specific-description
color: "#9FED58"
weight: 10