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);
}
}