Skip to content

Commit

Permalink
job slots console (#476)
Browse files Browse the repository at this point in the history
  • Loading branch information
MilonPL authored Dec 14, 2024
1 parent 991cab8 commit 0b776fa
Show file tree
Hide file tree
Showing 12 changed files with 705 additions and 0 deletions.
33 changes: 33 additions & 0 deletions Content.Client/_CD/JobSlotsConsole/JobRow.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<BoxContainer xmlns="https://spacestation14.io"
Orientation="Horizontal"
HorizontalExpand="True"
MinHeight="32"
Margin="8 0 8 2">
<BoxContainer Name="JobTitleContainer"
Orientation="Horizontal"
HorizontalExpand="True"
VerticalAlignment="Center">
<TextureRect Name="JobIcon"
TextureScale="2 2"
VerticalAlignment="Center"
Margin="0 0 4 0" />
<Label Name="JobNameLabel"
HorizontalExpand="True"
VerticalAlignment="Center" />
</BoxContainer>
<Button Name="DecreaseButton"
Text="-"
MinWidth="32" />
<Label Name="SlotsLabel"
MinWidth="60"
Align="Center"
VerticalAlignment="Center" />
<Button Name="IncreaseButton"
Text="+"
MinWidth="32"
Margin="0 0 4 0" />
<Button Name="ToggleInfiniteButton"
Text=""
MinWidth="32"
Visible="False" />
</BoxContainer>
87 changes: 87 additions & 0 deletions Content.Client/_CD/JobSlotsConsole/JobRow.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using Content.Shared._CD.JobSlotsConsole;
using Content.Shared.Roles;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;

namespace Content.Client._CD.JobSlotsConsole;

[GenerateTypedNameReferences]
public sealed partial class JobRow : BoxContainer
{
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;

public event Action<JobSlotAdjustment>? OnAdjustPressed;
public string JobName { get; }

public JobRow(ProtoId<JobPrototype> job, int? slots, bool blacklisted)
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);

var spriteSystem = _entitySystem.GetEntitySystem<SpriteSystem>();

var proto = _protoManager.Index(job);
JobName = Loc.GetString(proto.Name);
JobNameLabel.Text = JobName;

// Set up job icon
if (_protoManager.TryIndex(proto.Icon, out var jobIcon))
{
JobIcon.Texture = spriteSystem.Frame0(jobIcon.Icon);
}

UpdateSlots(slots, blacklisted);

DecreaseButton.OnPressed += OnDecrease;
IncreaseButton.OnPressed += OnIncrease;
ToggleInfiniteButton.OnPressed += OnToggleInfinite;
}

private void OnDecrease(BaseButton.ButtonEventArgs args)
{
OnAdjustPressed?.Invoke(JobSlotAdjustment.Decrease);
}

private void OnIncrease(BaseButton.ButtonEventArgs args)
{
OnAdjustPressed?.Invoke(JobSlotAdjustment.Increase);
}

private void OnToggleInfinite(BaseButton.ButtonEventArgs args)
{
var currentInfinite = SlotsLabel.Text == "∞";
OnAdjustPressed?.Invoke(currentInfinite ? JobSlotAdjustment.SetFinite : JobSlotAdjustment.SetInfinite);
}

public void ShowDebugControls(bool show)
{
ToggleInfiniteButton.Visible = show;
}

private void UpdateSlots(int? slots, bool blacklisted)
{
SlotsLabel.Text = slots?.ToString() ?? "∞";

// Disable buttons if blacklisted or at limits
DecreaseButton.Disabled = blacklisted || slots is 0 or null;
IncreaseButton.Disabled = blacklisted || slots == null;
ToggleInfiniteButton.Disabled = blacklisted;

if (blacklisted)
{
DecreaseButton.ToolTip = Loc.GetString("job-slots-console-blacklisted");
IncreaseButton.ToolTip = Loc.GetString("job-slots-console-blacklisted");
ToggleInfiniteButton.ToolTip = Loc.GetString("job-slots-console-blacklisted");
}
else
{
DecreaseButton.ToolTip = null;
IncreaseButton.ToolTip = null;
ToggleInfiniteButton.ToolTip = null;
}
}
}
49 changes: 49 additions & 0 deletions Content.Client/_CD/JobSlotsConsole/JobSlotsConsoleBUI.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Content.Shared._CD.JobSlotsConsole;
using Content.Shared.Roles;
using Robust.Shared.Prototypes;

namespace Content.Client._CD.JobSlotsConsole;

public sealed class JobSlotsConsoleBoundUserInterface : BoundUserInterface
{

private JobSlotsConsoleMenu? _menu;

public JobSlotsConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}

protected override void Open()
{
base.Open();

_menu = new JobSlotsConsoleMenu();
_menu.OpenCentered();
_menu.OnClose += Close;
_menu.OnAdjustPressed += AdjustSlot;
}

private void AdjustSlot(ProtoId<JobPrototype> jobId, JobSlotAdjustment adjustment)
{
SendMessage(new JobSlotsConsoleAdjustMessage(jobId, adjustment));
}

protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);

if (state is not JobSlotsConsoleState cast)
return;

_menu?.UpdateState(cast);
}

protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_menu?.Close();
_menu = null;
}
}
21 changes: 21 additions & 0 deletions Content.Client/_CD/JobSlotsConsole/JobSlotsConsoleMenu.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'job-slots-console-title'}"
SetSize="600 400"
MinSize="600 300">
<BoxContainer Orientation="Vertical" Margin="4">
<LineEdit Name="SearchBar"
PlaceHolder="{Loc 'job-slots-console-search-placeholder'}"
HorizontalExpand="True"
Margin="0 0 0 4" />

<ScrollContainer VerticalExpand="True" HorizontalExpand="True">
<BoxContainer Name="DepartmentsList"
Orientation="Vertical"
VerticalExpand="True"
HorizontalExpand="True">
<!-- Populated with departments -->
</BoxContainer>
</ScrollContainer>
</BoxContainer>
</controls:FancyWindow>
141 changes: 141 additions & 0 deletions Content.Client/_CD/JobSlotsConsole/JobSlotsConsoleMenu.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
using Content.Client.Administration.UI.CustomControls;
using Content.Client.UserInterface.Controls;
using Content.Shared.Roles;
using Content.Shared._CD.JobSlotsConsole;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using System.Linq;

namespace Content.Client._CD.JobSlotsConsole;

[GenerateTypedNameReferences]
public sealed partial class JobSlotsConsoleMenu : FancyWindow
{
[Dependency] private readonly IPrototypeManager _protoManager = default!;

private readonly Dictionary<ProtoId<DepartmentPrototype>, List<JobRow>> _departmentRows = new();
private readonly Dictionary<ProtoId<DepartmentPrototype>, Label> _departmentLabels = new();
private JobSlotsConsoleState? _currentState;

public event Action<ProtoId<JobPrototype>, JobSlotAdjustment>? OnAdjustPressed;

public JobSlotsConsoleMenu()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);

SearchBar.OnTextChanged += _ => UpdateJobList();
}

public void UpdateState(JobSlotsConsoleState state)
{
_currentState = state;

DepartmentsList.RemoveAllChildren();
_departmentRows.Clear();
_departmentLabels.Clear();

var jobsByDepartment = new Dictionary<DepartmentPrototype, List<(JobPrototype proto, int? slots, bool blacklisted)>>();

foreach (var (jobId, slots) in state.Jobs)
{
var proto = _protoManager.Index(jobId);
var department = GetPrimaryDepartmentForJob(proto.ID);

if (department is not { } dept)
continue;

if (!jobsByDepartment.TryGetValue(dept, out var jobs))
{
jobs = new List<(JobPrototype, int?, bool)>();
jobsByDepartment[dept] = jobs;
}

var blacklisted = state.BlacklistedJobs.Contains(jobId);
jobs.Add((proto, slots, blacklisted));
}

// Sort and add departments
foreach (var (department, jobs) in jobsByDepartment.OrderBy(x => x.Key, DepartmentUIComparer.Instance))
{
var sortedJobs = jobs
.OrderByDescending(x => x.proto.RealDisplayWeight)
.ThenBy(x => x.proto.LocalizedName)
.ToList();

AddDepartmentSection(department, sortedJobs);
}

UpdateJobList();
}

private void AddDepartmentSection(DepartmentPrototype department, List<(JobPrototype proto, int? slots, bool blacklisted)> jobs)
{
var departmentBox = new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Vertical,
HorizontalExpand = true,
Margin = new Thickness(0, 5),
};

// Add department header
var label = new Label
{
Text = Loc.GetString(department.Name),
HorizontalAlignment = HAlignment.Center,
StyleClasses = { "LabelHeading" },
ModulateSelfOverride = department.Color,
FontColorOverride = department.Color, // css trolling
};

_departmentLabels[department.ID] = label;
departmentBox.AddChild(label);
departmentBox.AddChild(new HSeparator { Margin = new Thickness(5, 3, 5, 5) });

// Add job rows
var jobRows = new List<JobRow>();
foreach (var (proto, slots, blacklisted) in jobs)
{
var row = new JobRow(proto.ID, slots, blacklisted);
row.OnAdjustPressed += adjustment =>
OnAdjustPressed?.Invoke(proto.ID, adjustment);
row.ShowDebugControls(_currentState?.Debug ?? false);
jobRows.Add(row);
departmentBox.AddChild(row);
}

_departmentRows[department.ID] = jobRows;
DepartmentsList.AddChild(departmentBox);
}

private void UpdateJobList()
{
var search = SearchBar.Text.Trim().ToLowerInvariant();

foreach (var (departmentId, rows) in _departmentRows)
{
var departmentLabel = _departmentLabels[departmentId];
var hasVisibleJobs = false;

foreach (var row in rows)
{
var visible = string.IsNullOrEmpty(search) ||
row.JobName.Contains(search, StringComparison.InvariantCultureIgnoreCase);
row.Visible = visible;
hasVisibleJobs |= visible;
}

departmentLabel.Parent!.Visible = hasVisibleJobs;
}
}

private DepartmentPrototype? GetPrimaryDepartmentForJob(ProtoId<JobPrototype> jobId)
{
return _protoManager.EnumeratePrototypes<DepartmentPrototype>()
.FirstOrDefault(d => d.Roles.Contains(jobId) && d.Primary)
?? _protoManager.EnumeratePrototypes<DepartmentPrototype>()
.FirstOrDefault(d => d.Roles.Contains(jobId));
}
}
33 changes: 33 additions & 0 deletions Content.Server/_CD/JobSlotsConsole/JobSlotsConsoleComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Content.Shared.Roles;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;

namespace Content.Server._CD.JobSlotsConsole;

[RegisterComponent]
public sealed partial class JobSlotsConsoleComponent : Component
{
/// <summary>
/// The station this console is linked to. Set when the station is detected.
/// </summary>
[DataField]
public EntityUid? Station;

/// <summary>
/// Jobs that cannot have their slots adjusted from this console.
/// </summary>
[DataField]
public HashSet<ProtoId<JobPrototype>> Blacklist = [];

/// <summary>
/// Whether this console has debug features enabled, like toggling infinite slots.
/// </summary>
[DataField]
public bool Debug;

/// <summary>
/// The sound to play if the player doesn't have access to change job slots.
/// </summary>
[DataField]
public SoundSpecifier DenySound = new SoundPathSpecifier("/Audio/Effects/Cargo/buzz_sigh.ogg");
}
Loading

0 comments on commit 0b776fa

Please sign in to comment.