Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

job slots console #476

Merged
merged 6 commits into from
Dec 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
Comment on lines +19 to +24
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.CreateWindow?


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

Comment on lines +25 to +30
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use SendPredictedMessage and make the whole thing predicted?

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;
}
Comment on lines +40 to +48
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obsolete

}
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));
}
}
Comment on lines +133 to +141
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should really be on jobs or something rather than here.

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
Loading