Skip to content

Commit

Permalink
Add JobRequirementOverride prototypes (#28607)
Browse files Browse the repository at this point in the history
* Add JobRequirementOverride prototypes

* a

* invert if

* Add override that takes in prototypes directly
  • Loading branch information
ElectroJr authored Jun 7, 2024
1 parent b3debf4 commit b632a65
Show file tree
Hide file tree
Showing 14 changed files with 145 additions and 48 deletions.
3 changes: 2 additions & 1 deletion Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,8 @@ public void RefreshAntags()
selector.Setup(items, title, 250, description, guides: antag.Guides);
selector.Select(Profile?.AntagPreferences.Contains(antag.ID) == true ? 0 : 1);

if (!_requirements.CheckRoleTime(antag.Requirements, out var reason))
var requirements = _entManager.System<SharedRoleSystem>().GetAntagRequirement(antag);
if (!_requirements.CheckRoleTime(requirements, out var reason))
{
selector.LockRequirements(reason);
Profile = Profile?.WithAntagPreference(antag.ID, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,13 @@ public bool IsAllowed(JobPrototype job, [NotNullWhen(false)] out FormattedMessag
if (player == null)
return true;

return CheckRoleTime(job.Requirements, out reason);
return CheckRoleTime(job, out reason);
}

public bool CheckRoleTime(JobPrototype job, [NotNullWhen(false)] out FormattedMessage? reason)
{
var reqs = _entManager.System<SharedRoleSystem>().GetJobRequirement(job);
return CheckRoleTime(reqs, out reason);
}

public bool CheckRoleTime(HashSet<JobRequirement>? requirements, [NotNullWhen(false)] out FormattedMessage? reason)
Expand Down
3 changes: 3 additions & 0 deletions Content.Server/Antag/AntagSelectionSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,9 @@ public AntagSelectionPlayerPool GetPlayerPool(Entity<AntagSelectionComponent> en
/// </summary>
public bool IsSessionValid(Entity<AntagSelectionComponent> ent, ICommonSession? session, AntagSelectionDefinition def, EntityUid? mind = null)
{
// TODO ROLE TIMERS
// Check if antag role requirements are met

if (session == null)
return true;

Expand Down
4 changes: 4 additions & 0 deletions Content.Server/Ghost/Roles/Components/GhostRoleComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ public sealed partial class GhostRoleComponent : Component

[DataField("rules")] private string _roleRules = "ghost-role-component-default-rules";

// TODO ROLE TIMERS
// Actually make use of / enforce this requirement?
// Why is this even here.
// Move to ghost role prototype & respect CCvars.GameRoleTimerOverride
[DataField("requirements")]
public HashSet<JobRequirement>? Requirements;

Expand Down
37 changes: 9 additions & 28 deletions Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,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 SharedRoleSystem _role = default!;

public override void Initialize()
{
Expand Down Expand Up @@ -197,7 +198,6 @@ private void OnGetDisallowedJobs(ref GetDisallowedJobsEvent ev)
public bool IsAllowed(ICommonSession player, string role)
{
if (!_prototypes.TryIndex<JobPrototype>(role, out var job) ||
job.Requirements == null ||
!_cfg.GetCVar(CCVars.GameRoleTimers))
return true;

Expand All @@ -224,19 +224,8 @@ public HashSet<ProtoId<JobPrototype>> GetDisallowedJobs(ICommonSession player)

foreach (var job in _prototypes.EnumeratePrototypes<JobPrototype>())
{
if (job.Requirements != null)
{
foreach (var requirement in job.Requirements)
{
if (JobRequirements.TryRequirementMet(requirement, playTimes, out _, EntityManager, _prototypes))
continue;

goto NoRole;
}
}

roles.Add(job.ID);
NoRole:;
if (JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes))
roles.Add(job.ID);
}

return roles;
Expand All @@ -257,22 +246,14 @@ public void RemoveDisallowedJobs(NetUserId userId, List<ProtoId<JobPrototype>> j

for (var i = 0; i < jobs.Count; i++)
{
var job = jobs[i];

if (!_prototypes.TryIndex(job, out var jobber) ||
jobber.Requirements == null ||
jobber.Requirements.Count == 0)
continue;

foreach (var requirement in jobber.Requirements)
if (_prototypes.TryIndex(jobs[i], out var job)
&& JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes))
{
if (JobRequirements.TryRequirementMet(requirement, playTimes, out _, EntityManager, _prototypes))
continue;

jobs.RemoveSwap(i);
i--;
break;
continue;
}

jobs.RemoveSwap(i);
i--;
}
}

Expand Down
7 changes: 7 additions & 0 deletions Content.Shared/CCVar/CCVars.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Content.Shared.Maps;
using Content.Shared.Roles;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.Physics.Components;
Expand Down Expand Up @@ -219,6 +220,12 @@ public static readonly CVarDef<bool>
public static readonly CVarDef<bool>
GameRoleTimers = CVarDef.Create("game.role_timers", true, CVar.SERVER | CVar.REPLICATED);

/// <summary>
/// Override default role requirements using a <see cref="JobRequirementOverridePrototype"/>
/// </summary>
public static readonly CVarDef<string>
GameRoleTimerOverride = CVarDef.Create("game.role_timer_override", "", CVar.SERVER | CVar.REPLICATED);

/// <summary>
/// If roles should be restricted based on whether or not they are whitelisted.
/// </summary>
Expand Down
12 changes: 6 additions & 6 deletions Content.Shared/Ghost/Roles/GhostRolePrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,24 @@ public sealed partial class GhostRolePrototype : IPrototype
/// <summary>
/// The name of the ghostrole.
/// </summary>
[DataField]
[DataField(required: true)]
public string Name { get; set; } = default!;

/// <summary>
/// The description of the ghostrole.
/// </summary>
[DataField]
[DataField(required: true)]
public string Description { get; set; } = default!;

/// <summary>
/// The entity prototype of the ghostrole
/// </summary>
[DataField]
public string EntityPrototype = default!;
[DataField(required: true)]
public EntProtoId EntityPrototype;

/// <summary>
/// Rules of the ghostrole
/// </summary>
[DataField]
[DataField(required: true)]
public string Rules = default!;
}
}
5 changes: 5 additions & 0 deletions Content.Shared/Ghost/Roles/GhostRolesEuiMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ public struct GhostRoleInfo
public string Name { get; set; }
public string Description { get; set; }
public string Rules { get; set; }

// TODO ROLE TIMERS
// Actually make use of / enforce this requirement?
// Why is this even here.
// Move to ghost role prototype & respect CCvars.GameRoleTimerOverride
public HashSet<JobRequirement>? Requirements { get; set; }

/// <inheritdoc cref="GhostRoleKind"/>
Expand Down
4 changes: 3 additions & 1 deletion Content.Shared/Roles/AntagPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ public sealed partial class AntagPrototype : IPrototype
/// <summary>
/// Requirements that must be met to opt in to this antag role.
/// </summary>
[DataField("requirements")]
// TODO ROLE TIMERS
// Actually check if the requirements are met. Because apparently this is actually unused.
[DataField, Access(typeof(SharedRoleSystem), Other = AccessPermissions.None)]
public HashSet<JobRequirement>? Requirements;

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion Content.Shared/Roles/JobPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public sealed partial class JobPrototype : IPrototype
[ViewVariables(VVAccess.ReadOnly)]
public string? LocalizedDescription => Description is null ? null : Loc.GetString(Description);

[DataField("requirements")]
[DataField, Access(typeof(SharedRoleSystem), Other = AccessPermissions.None)]
public HashSet<JobRequirement>? Requirements;

[DataField("joinNotifyCrew")]
Expand Down
20 changes: 20 additions & 0 deletions Content.Shared/Roles/JobRequirementOverridePrototype.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Robust.Shared.Prototypes;

namespace Content.Shared.Roles;

/// <summary>
/// Collection of job, antag, and ghost-role job requirements for per-server requirement overrides.
/// </summary>
[Prototype]
public sealed partial class JobRequirementOverridePrototype : IPrototype
{
[ViewVariables]
[IdDataField]
public string ID { get; private set; } = default!;

[DataField]
public Dictionary<ProtoId<JobPrototype>, HashSet<JobRequirement>> Jobs = new ();

[DataField]
public Dictionary<ProtoId<AntagPrototype>, HashSet<JobRequirement>> Antags = new ();
}
20 changes: 11 additions & 9 deletions Content.Shared/Roles/JobRequirements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,18 @@ public static class JobRequirements
{
public static bool TryRequirementsMet(
JobPrototype job,
Dictionary<string, TimeSpan> playTimes,
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 (job.Requirements == null)
if (requirements == null)
return true;

foreach (var requirement in job.Requirements)
foreach (var requirement in requirements)
{
if (!TryRequirementMet(requirement, playTimes, out reason, entManager, prototypes))
return false;
Expand Down Expand Up @@ -130,7 +132,7 @@ public static bool TryRequirementMet(
if (deptDiff <= 0)
return true;

reason = FormattedMessage.FromMarkup(Loc.GetString(
reason = FormattedMessage.FromMarkupPermissive(Loc.GetString(
"role-timer-department-insufficient",
("time", Math.Ceiling(deptDiff)),
("department", Loc.GetString(deptRequirement.Department)),
Expand All @@ -141,7 +143,7 @@ public static bool TryRequirementMet(
{
if (deptDiff <= 0)
{
reason = FormattedMessage.FromMarkup(Loc.GetString(
reason = FormattedMessage.FromMarkupPermissive(Loc.GetString(
"role-timer-department-too-high",
("time", -deptDiff),
("department", Loc.GetString(deptRequirement.Department)),
Expand All @@ -161,7 +163,7 @@ public static bool TryRequirementMet(
if (overallDiff <= 0 || overallTime >= overallRequirement.Time)
return true;

reason = FormattedMessage.FromMarkup(Loc.GetString(
reason = FormattedMessage.FromMarkupPermissive(Loc.GetString(
"role-timer-overall-insufficient",
("time", Math.Ceiling(overallDiff))));
return false;
Expand All @@ -170,7 +172,7 @@ public static bool TryRequirementMet(
{
if (overallDiff <= 0 || overallTime >= overallRequirement.Time)
{
reason = FormattedMessage.FromMarkup(Loc.GetString("role-timer-overall-too-high", ("time", -overallDiff)));
reason = FormattedMessage.FromMarkupPermissive(Loc.GetString("role-timer-overall-too-high", ("time", -overallDiff)));
return false;
}

Expand All @@ -197,7 +199,7 @@ public static bool TryRequirementMet(
if (roleDiff <= 0)
return true;

reason = FormattedMessage.FromMarkup(Loc.GetString(
reason = FormattedMessage.FromMarkupPermissive(Loc.GetString(
"role-timer-role-insufficient",
("time", Math.Ceiling(roleDiff)),
("job", Loc.GetString(proto)),
Expand All @@ -208,7 +210,7 @@ public static bool TryRequirementMet(
{
if (roleDiff <= 0)
{
reason = FormattedMessage.FromMarkup(Loc.GetString(
reason = FormattedMessage.FromMarkupPermissive(Loc.GetString(
"role-timer-role-too-high",
("time", -roleDiff),
("job", Loc.GetString(proto)),
Expand Down
52 changes: 51 additions & 1 deletion Content.Shared/Roles/SharedRoleSystem.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System.Linq;
using Content.Shared.Administration.Logs;
using Content.Shared.CCVar;
using Content.Shared.Database;
using Content.Shared.Ghost.Roles;
using Content.Shared.Mind;
using Content.Shared.Roles.Jobs;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;

Expand All @@ -16,14 +18,30 @@ public abstract class SharedRoleSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototypes = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedMindSystem _minds = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;

// TODO please lord make role entities
private readonly HashSet<Type> _antagTypes = new();

private JobRequirementOverridePrototype? _requirementOverride;

public override void Initialize()
{
// TODO make roles entities
SubscribeLocalEvent<JobComponent, MindGetAllRolesEvent>(OnJobGetAllRoles);
Subs.CVar(_cfg, CCVars.GameRoleTimerOverride, SetRequirementOverride, true);
}

private void SetRequirementOverride(string value)
{
if (string.IsNullOrEmpty(value))
{
_requirementOverride = null;
return;
}

if (!_prototypes.TryIndex(value, out _requirementOverride ))
Log.Error($"Unknown JobRequirementOverridePrototype: {value}");
}

private void OnJobGetAllRoles(EntityUid uid, JobComponent component, ref MindGetAllRolesEvent args)
Expand Down Expand Up @@ -253,4 +271,36 @@ public void MindPlaySound(EntityUid mindId, SoundSpecifier? sound, MindComponent
if (Resolve(mindId, ref mind) && mind.Session != null)
_audio.PlayGlobal(sound, mind.Session);
}

public HashSet<JobRequirement>? GetJobRequirement(JobPrototype job)
{
if (_requirementOverride != null && _requirementOverride.Jobs.TryGetValue(job.ID, out var req))
return req;

return job.Requirements;
}

public HashSet<JobRequirement>? GetJobRequirement(ProtoId<JobPrototype> job)
{
if (_requirementOverride != null && _requirementOverride.Jobs.TryGetValue(job, out var req))
return req;

return _prototypes.Index(job).Requirements;
}

public HashSet<JobRequirement>? GetAntagRequirement(ProtoId<AntagPrototype> antag)
{
if (_requirementOverride != null && _requirementOverride.Antags.TryGetValue(antag, out var req))
return req;

return _prototypes.Index(antag).Requirements;
}

public HashSet<JobRequirement>? GetAntagRequirement(AntagPrototype antag)
{
if (_requirementOverride != null && _requirementOverride.Antags.TryGetValue(antag.ID, out var req))
return req;

return antag.Requirements;
}
}
16 changes: 16 additions & 0 deletions Resources/Prototypes/Roles/requirement_overrides.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
- type: jobRequirementOverride
id: Reduced
jobs:
Captain:
- !type:DepartmentTimeRequirement
department: Engineering
time: 3600 # 1 hours
- !type:DepartmentTimeRequirement
department: Medical
time: 3600 # 1 hours
- !type:DepartmentTimeRequirement
department: Security
time: 3600 # 1 hours
- !type:DepartmentTimeRequirement
department: Command
time: 3600 # 1 hour

0 comments on commit b632a65

Please sign in to comment.