using System.Diagnostics.CodeAnalysis; using Content.Shared.Access; using Content.Shared.Guidebook; using Content.Shared.Players.PlayTimeTracking; using Content.Shared.StatusIcon; using Robust.Shared.Prototypes; namespace Content.Shared.Roles; /// /// Describes information for a single job on the station. /// [Prototype] public sealed partial class JobPrototype : IPrototype { [ViewVariables] [IdDataField] public string ID { get; private set; } = default!; [DataField(required: true)] public ProtoId PlayTimeTracker = string.Empty; /// /// Who is the supervisor for this job. /// [DataField] public LocId Supervisors = "job-supervisors-nobody"; /// /// The name of this job as displayed to players. /// [DataField] public string Name = string.Empty; [ViewVariables(VVAccess.ReadOnly)] public string LocalizedName => Loc.GetString(Name); /// /// The name of this job as displayed to players. /// [DataField] public string? Description; [ViewVariables(VVAccess.ReadOnly)] public string? LocalizedDescription => Description is null ? null : Loc.GetString(Description); /// /// Requirements for the job. /// [DataField, Access(typeof(SharedRoleSystem), Other = AccessPermissions.None)] public HashSet? Requirements; /// /// When true - the station will have announcement about arrival of this player. /// [DataField] public bool JoinNotifyCrew; /// /// When true - the player will recieve a message about importancy of their job. /// [DataField] public bool RequireAdminNotify; /// /// Should this job appear in preferences menu? /// [DataField] public bool SetPreference = true; /// /// Should the selected traits be applied for this job? /// [DataField] public bool ApplyTraits = true; /// /// Whether this job should show in the ID Card Console. /// If set to null, it will default to SetPreference's value. /// [DataField] public bool? OverrideConsoleVisibility; /// /// A numerical score for how much easier this job is for antagonists. /// For traitors, reduces starting TC by this amount. Other gamemodes can use it for whatever they find fitting. /// [DataField] public int AntagAdvantage; [DataField] public ProtoId? StartingGear { get; private set; } /// /// Use this to spawn in as a non-humanoid (borg, test subject, etc.) /// Starting gear will be ignored. /// If you want to just add special attributes to a humanoid, use AddComponentSpecial instead. /// [DataField] public EntProtoId? JobEntity; /// /// Entity to use as a preview in the lobby/character editor. /// Same restrictions as apply. /// [DataField] public EntProtoId? JobPreviewEntity; [DataField] public ProtoId Icon = "JobIconUnknown"; [DataField(serverOnly: true)] public JobSpecial[] Special { get; private set; } = Array.Empty(); [DataField] public IReadOnlyCollection> Access = Array.Empty>(); [DataField] public IReadOnlyCollection> AccessGroups = Array.Empty>(); [DataField] public IReadOnlyCollection> ExtendedAccess = Array.Empty>(); [DataField] public IReadOnlyCollection> ExtendedAccessGroups = Array.Empty>(); [DataField] public bool Whitelisted; /// /// Optional list of guides associated with this role. If the guides are opened, the first entry in this list /// will be used to select the currently selected guidebook. /// [DataField] public List>? Guides; } /// /// Sorts s appropriately for display using a map's job weighting profile. /// public sealed class JobUIComparer : IComparer { private readonly IReadOnlyDictionary, int> _weights; private JobUIComparer(IReadOnlyDictionary, int> weights) { _weights = weights; } /// /// Creates a comparer when the global fallback profile exists. /// Without one, callers should retain the source order rather than sorting jobs. /// public static bool TryCreate( IPrototypeManager prototypes, ProtoId? jobWeights, [NotNullWhen(true)] out JobUIComparer? comparer) { if (!prototypes.TryIndex(JobWeightPrototype.Default, out var defaultProfile)) { comparer = null; return false; } var weights = new Dictionary, int>(defaultProfile.Weights); if (jobWeights != null && prototypes.TryIndex(jobWeights.Value, out var mapProfile)) { foreach (var (job, weight) in mapProfile.Weights) { weights[job] = weight; } } comparer = new JobUIComparer(weights); return true; } /// /// Gets the configured display weight for a job, if one exists. /// public int? GetWeight(JobPrototype job) { return _weights.TryGetValue(job.ID, out var weight) ? weight : null; } public int Compare(JobPrototype? x, JobPrototype? y) { if (ReferenceEquals(x, y)) return 0; if (ReferenceEquals(null, y)) return 1; if (ReferenceEquals(null, x)) return -1; var xWeight = GetWeight(x); var yWeight = GetWeight(y); if (xWeight == null || yWeight == null) { return 0; } var cmp = -xWeight.Value.CompareTo(yWeight.Value); if (cmp != 0) return cmp; return string.Compare(x.ID, y.ID, StringComparison.Ordinal); } }