Station records (#8720)
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
using Content.Client.Access.Components;
|
using Content.Client.Access.Components;
|
||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
using Content.Shared.Containers.ItemSlots;
|
using Content.Shared.Containers.ItemSlots;
|
||||||
|
using Content.Shared.CrewManifest;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using static Content.Shared.Access.Components.SharedIdCardConsoleComponent;
|
using static Content.Shared.Access.Components.SharedIdCardConsoleComponent;
|
||||||
@@ -36,6 +37,7 @@ namespace Content.Client.Access.UI
|
|||||||
|
|
||||||
_window = new IdCardConsoleWindow(this, _prototypeManager, accessLevels) {Title = _entityManager.GetComponent<MetaDataComponent>(Owner.Owner).EntityName};
|
_window = new IdCardConsoleWindow(this, _prototypeManager, accessLevels) {Title = _entityManager.GetComponent<MetaDataComponent>(Owner.Owner).EntityName};
|
||||||
|
|
||||||
|
_window.CrewManifestButton.OnPressed += _ => SendMessage(new CrewManifestOpenUiMessage());
|
||||||
_window.PrivilegedIdButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(PrivilegedIdCardSlotId));
|
_window.PrivilegedIdButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(PrivilegedIdCardSlotId));
|
||||||
_window.TargetIdButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(TargetIdCardSlotId));
|
_window.TargetIdButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(TargetIdCardSlotId));
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
<DefaultWindow xmlns="https://spacestation14.io"
|
<DefaultWindow xmlns="https://spacestation14.io"
|
||||||
MinSize="650 290">
|
MinSize="650 290">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<GridContainer Columns="3">
|
<GridContainer Columns="2">
|
||||||
<Label Text="{Loc 'id-card-console-window-privileged-id'}" />
|
<GridContainer Columns="3" HorizontalExpand="True">
|
||||||
<Button Name="PrivilegedIdButton" Access="Public"/>
|
<Label Text="{Loc 'id-card-console-window-privileged-id'}" />
|
||||||
<Label Name="PrivilegedIdLabel" />
|
<Button Name="PrivilegedIdButton" Access="Public"/>
|
||||||
|
<Label Name="PrivilegedIdLabel" />
|
||||||
|
|
||||||
<Label Text="{Loc 'id-card-console-window-target-id'}" />
|
<Label Text="{Loc 'id-card-console-window-target-id'}" />
|
||||||
<Button Name="TargetIdButton" Access="Public"/>
|
<Button Name="TargetIdButton" Access="Public"/>
|
||||||
<Label Name="TargetIdLabel" />
|
<Label Name="TargetIdLabel" />
|
||||||
|
</GridContainer>
|
||||||
|
<BoxContainer Orientation="Vertical">
|
||||||
|
<Button Name="CrewManifestButton" Access="Public" Text="{Loc 'crew-manifest-button-label'}" />
|
||||||
|
</BoxContainer>
|
||||||
</GridContainer>
|
</GridContainer>
|
||||||
<Control MinSize="0 8" />
|
<Control MinSize="0 8" />
|
||||||
<GridContainer Columns="3" HSeparationOverride="4">
|
<GridContainer Columns="3" HSeparationOverride="4">
|
||||||
@@ -21,6 +26,10 @@
|
|||||||
<Button Name="JobTitleSaveButton" Text="{Loc 'id-card-console-window-save-button'}" Disabled="True" />
|
<Button Name="JobTitleSaveButton" Text="{Loc 'id-card-console-window-save-button'}" Disabled="True" />
|
||||||
</GridContainer>
|
</GridContainer>
|
||||||
<Control MinSize="0 8" />
|
<Control MinSize="0 8" />
|
||||||
|
<GridContainer Columns="2">
|
||||||
|
<Label Text="{Loc 'id-card-console-window-job-selection-label'}" />
|
||||||
|
<OptionButton Name="JobPresetOptionButton" />
|
||||||
|
</GridContainer>
|
||||||
<GridContainer Name="AccessLevelGrid" Columns="5" HorizontalAlignment="Center">
|
<GridContainer Name="AccessLevelGrid" Columns="5" HorizontalAlignment="Center">
|
||||||
|
|
||||||
<!-- Access level buttons are added here by the C# code -->
|
<!-- Access level buttons are added here by the C# code -->
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Shared.Access;
|
using Content.Shared.Access;
|
||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
|
using Content.Shared.Roles;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
@@ -16,9 +17,12 @@ namespace Content.Client.Access.UI
|
|||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class IdCardConsoleWindow : DefaultWindow
|
public sealed partial class IdCardConsoleWindow : DefaultWindow
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
|
||||||
private readonly IdCardConsoleBoundUserInterface _owner;
|
private readonly IdCardConsoleBoundUserInterface _owner;
|
||||||
|
|
||||||
private readonly Dictionary<string, Button> _accessButtons = new();
|
private readonly Dictionary<string, Button> _accessButtons = new();
|
||||||
|
private readonly List<string> _jobPrototypeIds = new();
|
||||||
|
|
||||||
private string? _lastFullName;
|
private string? _lastFullName;
|
||||||
private string? _lastJobTitle;
|
private string? _lastJobTitle;
|
||||||
@@ -26,6 +30,7 @@ namespace Content.Client.Access.UI
|
|||||||
public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, IPrototypeManager prototypeManager, List<string> accessLevels)
|
public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, IPrototypeManager prototypeManager, List<string> accessLevels)
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
_owner = owner;
|
_owner = owner;
|
||||||
|
|
||||||
@@ -43,6 +48,21 @@ namespace Content.Client.Access.UI
|
|||||||
};
|
};
|
||||||
JobTitleSaveButton.OnPressed += _ => SubmitData();
|
JobTitleSaveButton.OnPressed += _ => SubmitData();
|
||||||
|
|
||||||
|
var jobs = _prototypeManager.EnumeratePrototypes<JobPrototype>();
|
||||||
|
|
||||||
|
foreach (var job in jobs)
|
||||||
|
{
|
||||||
|
if (!job.SetPreference)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_jobPrototypeIds.Add(job.ID);
|
||||||
|
JobPresetOptionButton.AddItem(Loc.GetString(job.Name), _jobPrototypeIds.Count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
JobPresetOptionButton.OnItemSelected += SelectJobPreset;
|
||||||
|
|
||||||
foreach (var access in accessLevels)
|
foreach (var access in accessLevels)
|
||||||
{
|
{
|
||||||
if (!prototypeManager.TryIndex<AccessLevelPrototype>(access, out var accessLevel))
|
if (!prototypeManager.TryIndex<AccessLevelPrototype>(access, out var accessLevel))
|
||||||
@@ -62,6 +82,56 @@ namespace Content.Client.Access.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ClearAllAccess()
|
||||||
|
{
|
||||||
|
foreach (var button in _accessButtons.Values)
|
||||||
|
{
|
||||||
|
if (button.Pressed)
|
||||||
|
{
|
||||||
|
button.Pressed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectJobPreset(OptionButton.ItemSelectedEventArgs args)
|
||||||
|
{
|
||||||
|
if (!_prototypeManager.TryIndex(_jobPrototypeIds[args.Id], out JobPrototype? job))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JobTitleLineEdit.Text = Loc.GetString(job.Name);
|
||||||
|
|
||||||
|
ClearAllAccess();
|
||||||
|
|
||||||
|
// this is a sussy way to do this
|
||||||
|
foreach (var access in job.Access)
|
||||||
|
{
|
||||||
|
if (_accessButtons.TryGetValue(access, out var button))
|
||||||
|
{
|
||||||
|
button.Pressed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var group in job.AccessGroups)
|
||||||
|
{
|
||||||
|
if (!_prototypeManager.TryIndex(group, out AccessGroupPrototype? groupPrototype))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var access in groupPrototype.Tags)
|
||||||
|
{
|
||||||
|
if (_accessButtons.TryGetValue(access, out var button))
|
||||||
|
{
|
||||||
|
button.Pressed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SubmitData();
|
||||||
|
}
|
||||||
|
|
||||||
public void UpdateState(IdCardConsoleBoundUserInterfaceState state)
|
public void UpdateState(IdCardConsoleBoundUserInterfaceState state)
|
||||||
{
|
{
|
||||||
PrivilegedIdButton.Text = state.IsPrivilegedIdPresent
|
PrivilegedIdButton.Text = state.IsPrivilegedIdPresent
|
||||||
@@ -100,6 +170,8 @@ namespace Content.Client.Access.UI
|
|||||||
|
|
||||||
JobTitleSaveButton.Disabled = !interfaceEnabled || !jobTitleDirty;
|
JobTitleSaveButton.Disabled = !interfaceEnabled || !jobTitleDirty;
|
||||||
|
|
||||||
|
JobPresetOptionButton.Disabled = !interfaceEnabled;
|
||||||
|
|
||||||
foreach (var (accessName, button) in _accessButtons)
|
foreach (var (accessName, button) in _accessButtons)
|
||||||
{
|
{
|
||||||
button.Disabled = !interfaceEnabled;
|
button.Disabled = !interfaceEnabled;
|
||||||
|
|||||||
51
Content.Client/CrewManifest/CrewManifestEui.cs
Normal file
51
Content.Client/CrewManifest/CrewManifestEui.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using Content.Client.Eui;
|
||||||
|
using Content.Client.GameTicking.Managers;
|
||||||
|
using Content.Shared.CrewManifest;
|
||||||
|
using Content.Shared.Eui;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace Content.Client.CrewManifest;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public sealed class CrewManifestEui : BaseEui
|
||||||
|
{
|
||||||
|
private readonly ClientGameTicker _gameTicker;
|
||||||
|
private readonly CrewManifestUi _window;
|
||||||
|
|
||||||
|
public CrewManifestEui()
|
||||||
|
{
|
||||||
|
_gameTicker = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ClientGameTicker>();
|
||||||
|
_window = new();
|
||||||
|
|
||||||
|
_window.OnClose += () =>
|
||||||
|
{
|
||||||
|
SendMessage(new CrewManifestEuiClosed());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Opened()
|
||||||
|
{
|
||||||
|
base.Opened();
|
||||||
|
|
||||||
|
_window.OpenCentered();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Closed()
|
||||||
|
{
|
||||||
|
base.Closed();
|
||||||
|
|
||||||
|
_window.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleState(EuiStateBase state)
|
||||||
|
{
|
||||||
|
base.HandleState(state);
|
||||||
|
|
||||||
|
if (state is not CrewManifestEuiState cast)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_window.Populate(cast.StationName, cast.Entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
82
Content.Client/CrewManifest/CrewManifestSystem.cs
Normal file
82
Content.Client/CrewManifest/CrewManifestSystem.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
using Content.Client.GameTicking.Managers;
|
||||||
|
using Content.Shared.CrewManifest;
|
||||||
|
using Content.Shared.Roles;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Client.CrewManifest;
|
||||||
|
|
||||||
|
public sealed class CrewManifestSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
|
||||||
|
private Dictionary<string, Dictionary<string, int>> _jobDepartmentLookup = new();
|
||||||
|
private HashSet<string> _departments = new();
|
||||||
|
|
||||||
|
public IReadOnlySet<string> Departments => _departments;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
BuildDepartmentLookup();
|
||||||
|
_prototypeManager.PrototypesReloaded += OnPrototypesReload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Shutdown()
|
||||||
|
{
|
||||||
|
_prototypeManager.PrototypesReloaded -= OnPrototypesReload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Requests a crew manifest from the server.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">EntityUid of the entity we're requesting the crew manifest from.</param>
|
||||||
|
public void RequestCrewManifest(EntityUid uid)
|
||||||
|
{
|
||||||
|
RaiseNetworkEvent(new RequestCrewManifestMessage(uid));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPrototypesReload(PrototypesReloadedEventArgs _)
|
||||||
|
{
|
||||||
|
_jobDepartmentLookup.Clear();
|
||||||
|
_departments.Clear();
|
||||||
|
|
||||||
|
BuildDepartmentLookup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BuildDepartmentLookup()
|
||||||
|
{
|
||||||
|
foreach (var department in _prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
|
||||||
|
{
|
||||||
|
_departments.Add(department.ID);
|
||||||
|
|
||||||
|
for (var i = 1; i <= department.Roles.Count; i++)
|
||||||
|
{
|
||||||
|
if (!_jobDepartmentLookup.TryGetValue(department.Roles[i - 1], out var departments))
|
||||||
|
{
|
||||||
|
departments = new();
|
||||||
|
_jobDepartmentLookup.Add(department.Roles[i - 1], departments);
|
||||||
|
}
|
||||||
|
|
||||||
|
departments.Add(department.ID, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetDepartmentOrder(string department, string jobPrototype)
|
||||||
|
{
|
||||||
|
if (!Departments.Contains(department))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_jobDepartmentLookup.TryGetValue(jobPrototype, out var departments))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return departments.TryGetValue(department, out var order)
|
||||||
|
? order
|
||||||
|
: -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Content.Client/CrewManifest/CrewManifestUi.xaml
Normal file
21
Content.Client/CrewManifest/CrewManifestUi.xaml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<DefaultWindow xmlns="https://spacestation14.io"
|
||||||
|
xmlns:ui="clr-namespace:Content.Client.HUD.UI"
|
||||||
|
Title="{Loc 'crew-manifest-window-title'}"
|
||||||
|
MinSize="450 750">
|
||||||
|
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
|
||||||
|
<ui:StripeBack Name="StationNameContainer">
|
||||||
|
<PanelContainer>
|
||||||
|
<Label Name="StationName" Align="Center" />
|
||||||
|
</PanelContainer>
|
||||||
|
</ui:StripeBack>
|
||||||
|
<BoxContainer HorizontalExpand="True" VerticalExpand="True">
|
||||||
|
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
|
||||||
|
<!-- this MIGHT have race conditions -->
|
||||||
|
<BoxContainer Name="CrewManifestListing" Orientation="Vertical" HorizontalExpand="True">
|
||||||
|
<Label Text="{Loc 'crew-manifest-no-valid-station'}" HorizontalExpand="True" />
|
||||||
|
</BoxContainer>
|
||||||
|
<!-- Crew manifest goes here. -->
|
||||||
|
</ScrollContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
</DefaultWindow>
|
||||||
186
Content.Client/CrewManifest/CrewManifestUi.xaml.cs
Normal file
186
Content.Client/CrewManifest/CrewManifestUi.xaml.cs
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Content.Shared.CCVar;
|
||||||
|
using Content.Shared.CrewManifest;
|
||||||
|
using Content.Shared.Roles;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.ResourceManagement;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Client.Utility;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.CrewManifest;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class CrewManifestUi : DefaultWindow
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||||
|
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||||
|
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||||
|
|
||||||
|
private readonly CrewManifestSystem _crewManifestSystem;
|
||||||
|
|
||||||
|
private EntityUid? _station;
|
||||||
|
|
||||||
|
public CrewManifestUi()
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
_crewManifestSystem = _entitySystemManager.GetEntitySystem<CrewManifestSystem>();
|
||||||
|
|
||||||
|
StationName.AddStyleClass("LabelBig");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Populate(string name, CrewManifestEntries? entries)
|
||||||
|
{
|
||||||
|
CrewManifestListing.DisposeAllChildren();
|
||||||
|
CrewManifestListing.RemoveAllChildren();
|
||||||
|
|
||||||
|
StationNameContainer.Visible = entries != null;
|
||||||
|
StationName.Text = name;
|
||||||
|
|
||||||
|
if (entries == null) return;
|
||||||
|
|
||||||
|
var entryList = SortEntries(entries);
|
||||||
|
|
||||||
|
foreach (var item in entryList)
|
||||||
|
{
|
||||||
|
CrewManifestListing.AddChild(new CrewManifestSection(item.section, item.entries, _resourceCache, _crewManifestSystem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<(string section, List<CrewManifestEntry> entries)> SortEntries(CrewManifestEntries entries)
|
||||||
|
{
|
||||||
|
var entryDict = new Dictionary<string, List<CrewManifestEntry>>();
|
||||||
|
|
||||||
|
foreach (var entry in entries.Entries)
|
||||||
|
{
|
||||||
|
foreach (var department in _prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
|
||||||
|
{
|
||||||
|
// this is a little expensive, and could be better
|
||||||
|
if (department.Roles.Contains(entry.JobPrototype))
|
||||||
|
{
|
||||||
|
entryDict.GetOrNew(department.ID).Add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var entryList = new List<(string section, List<CrewManifestEntry> entries)>();
|
||||||
|
|
||||||
|
foreach (var (section, listing) in entryDict)
|
||||||
|
{
|
||||||
|
entryList.Add((section, listing));
|
||||||
|
}
|
||||||
|
|
||||||
|
var sortOrder = _configManager.GetCVar(CCVars.CrewManifestOrdering).Split(",").ToList();
|
||||||
|
|
||||||
|
entryList.Sort((a, b) =>
|
||||||
|
{
|
||||||
|
var ai = sortOrder.IndexOf(a.section);
|
||||||
|
var bi = sortOrder.IndexOf(b.section);
|
||||||
|
|
||||||
|
// this is up here so -1 == -1 occurs first
|
||||||
|
if (ai == bi)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ai == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (bi == -1)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return ai.CompareTo(bi);
|
||||||
|
});
|
||||||
|
|
||||||
|
return entryList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class CrewManifestSection : BoxContainer
|
||||||
|
{
|
||||||
|
public CrewManifestSection(string sectionTitle, List<CrewManifestEntry> entries, IResourceCache cache, CrewManifestSystem crewManifestSystem)
|
||||||
|
{
|
||||||
|
Orientation = LayoutOrientation.Vertical;
|
||||||
|
HorizontalExpand = true;
|
||||||
|
|
||||||
|
AddChild(new Label()
|
||||||
|
{
|
||||||
|
StyleClasses = { "LabelBig" },
|
||||||
|
Text = Loc.GetString(sectionTitle)
|
||||||
|
});
|
||||||
|
|
||||||
|
entries.Sort((a, b) =>
|
||||||
|
{
|
||||||
|
var posA = crewManifestSystem.GetDepartmentOrder(sectionTitle, a.JobPrototype);
|
||||||
|
var posB = crewManifestSystem.GetDepartmentOrder(sectionTitle, b.JobPrototype);
|
||||||
|
|
||||||
|
return posA.CompareTo(posB);
|
||||||
|
});
|
||||||
|
|
||||||
|
var gridContainer = new GridContainer()
|
||||||
|
{
|
||||||
|
HorizontalExpand = true,
|
||||||
|
Columns = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
AddChild(gridContainer);
|
||||||
|
|
||||||
|
var path = new ResourcePath("/Textures/Interface/Misc/job_icons.rsi");
|
||||||
|
cache.TryGetResource(path, out RSIResource? rsi);
|
||||||
|
|
||||||
|
foreach (var entry in entries)
|
||||||
|
{
|
||||||
|
var name = new Label()
|
||||||
|
{
|
||||||
|
HorizontalExpand = true,
|
||||||
|
Text = entry.Name
|
||||||
|
};
|
||||||
|
|
||||||
|
var titleContainer = new BoxContainer()
|
||||||
|
{
|
||||||
|
Orientation = LayoutOrientation.Horizontal,
|
||||||
|
HorizontalExpand = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var title = new Label()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString(entry.JobTitle)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if (rsi != null)
|
||||||
|
{
|
||||||
|
var icon = new TextureRect()
|
||||||
|
{
|
||||||
|
TextureScale = (2, 2),
|
||||||
|
Stretch = TextureRect.StretchMode.KeepCentered
|
||||||
|
};
|
||||||
|
|
||||||
|
if (rsi.RSI.TryGetState(entry.JobIcon, out _))
|
||||||
|
{
|
||||||
|
var specifier = new SpriteSpecifier.Rsi(path, entry.JobIcon);
|
||||||
|
icon.Texture = specifier.Frame0();
|
||||||
|
}
|
||||||
|
else if (rsi.RSI.TryGetState("Unknown", out _))
|
||||||
|
{
|
||||||
|
var specifier = new SpriteSpecifier.Rsi(path, "Unknown");
|
||||||
|
icon.Texture = specifier.Frame0();
|
||||||
|
}
|
||||||
|
|
||||||
|
titleContainer.AddChild(icon);
|
||||||
|
titleContainer.AddChild(title);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
titleContainer.AddChild(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
gridContainer.AddChild(name);
|
||||||
|
gridContainer.AddChild(titleContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Content.Client.CrewManifest;
|
||||||
|
using Content.Client.Eui;
|
||||||
using Content.Client.GameTicking.Managers;
|
using Content.Client.GameTicking.Managers;
|
||||||
using Content.Client.HUD.UI;
|
using Content.Client.HUD.UI;
|
||||||
|
using Content.Shared.CCVar;
|
||||||
|
using Content.Shared.CrewManifest;
|
||||||
using Content.Shared.Roles;
|
using Content.Shared.Roles;
|
||||||
using Robust.Client.Console;
|
using Robust.Client.Console;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using Robust.Client.Utility;
|
using Robust.Client.Utility;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||||
@@ -17,6 +22,7 @@ namespace Content.Client.LateJoin
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||||
|
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||||
|
|
||||||
public event Action<(EntityUid, string)> SelectedId;
|
public event Action<(EntityUid, string)> SelectedId;
|
||||||
|
|
||||||
@@ -109,6 +115,21 @@ namespace Content.Client.LateJoin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (_configManager.GetCVar<bool>(CCVars.CrewManifestWithoutEntity))
|
||||||
|
{
|
||||||
|
var crewManifestButton = new Button()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("crew-manifest-button-label")
|
||||||
|
};
|
||||||
|
crewManifestButton.OnPressed += args =>
|
||||||
|
{
|
||||||
|
EntitySystem.Get<CrewManifestSystem>().RequestCrewManifest(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
_base.AddChild(crewManifestButton);
|
||||||
|
}
|
||||||
|
|
||||||
var jobListScroll = new ScrollContainer()
|
var jobListScroll = new ScrollContainer()
|
||||||
{
|
{
|
||||||
VerticalExpand = true,
|
VerticalExpand = true,
|
||||||
|
|||||||
@@ -1,18 +1,24 @@
|
|||||||
using Content.Client.Message;
|
using Content.Client.Message;
|
||||||
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.Containers.ItemSlots;
|
using Content.Shared.Containers.ItemSlots;
|
||||||
|
using Content.Shared.CrewManifest;
|
||||||
using Content.Shared.PDA;
|
using Content.Shared.PDA;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
|
||||||
namespace Content.Client.PDA
|
namespace Content.Client.PDA
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public sealed class PDABoundUserInterface : BoundUserInterface
|
public sealed class PDABoundUserInterface : BoundUserInterface
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||||
|
|
||||||
private PDAMenu? _menu;
|
private PDAMenu? _menu;
|
||||||
|
|
||||||
public PDABoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
public PDABoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||||
{
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
@@ -27,6 +33,15 @@ namespace Content.Client.PDA
|
|||||||
SendMessage(new PDAToggleFlashlightMessage());
|
SendMessage(new PDAToggleFlashlightMessage());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (_configManager.GetCVar(CCVars.CrewManifestUnsecure))
|
||||||
|
{
|
||||||
|
_menu.CrewManifestButton.Visible = true;
|
||||||
|
_menu.CrewManifestButton.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
SendMessage(new CrewManifestOpenUiMessage());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
_menu.EjectIdButton.OnPressed += _ =>
|
_menu.EjectIdButton.OnPressed += _ =>
|
||||||
{
|
{
|
||||||
SendMessage(new ItemSlotButtonPressedEvent(PDAComponent.PDAIdSlotId));
|
SendMessage(new ItemSlotButtonPressedEvent(PDAComponent.PDAIdSlotId));
|
||||||
|
|||||||
@@ -37,6 +37,10 @@
|
|||||||
Access="Public"
|
Access="Public"
|
||||||
Text="{Loc 'comp-pda-ui-toggle-flashlight-button'}"
|
Text="{Loc 'comp-pda-ui-toggle-flashlight-button'}"
|
||||||
ToggleMode="True" />
|
ToggleMode="True" />
|
||||||
|
<Button Name="CrewManifestButton"
|
||||||
|
Access="Public"
|
||||||
|
Text="{Loc 'crew-manifest-button-label'}"
|
||||||
|
Visible="False" />
|
||||||
<Button Name="ActivateUplinkButton"
|
<Button Name="ActivateUplinkButton"
|
||||||
Access="Public"
|
Access="Public"
|
||||||
Text="{Loc 'pda-bound-user-interface-uplink-tab-title'}" />
|
Text="{Loc 'pda-bound-user-interface-uplink-tab-title'}" />
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using Content.Shared.StationRecords;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Client.StationRecords;
|
||||||
|
|
||||||
|
public sealed class GeneralStationRecordConsoleBoundUserInterface : BoundUserInterface
|
||||||
|
{
|
||||||
|
private GeneralStationRecordConsoleWindow? _window = default!;
|
||||||
|
|
||||||
|
public GeneralStationRecordConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected override void Open()
|
||||||
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
|
_window = new();
|
||||||
|
_window.OnKeySelected += OnKeySelected;
|
||||||
|
_window.OnClose += Close;
|
||||||
|
|
||||||
|
_window.OpenCentered();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnKeySelected(StationRecordKey? key)
|
||||||
|
{
|
||||||
|
SendMessage(new SelectGeneralStationRecord(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
|
{
|
||||||
|
base.UpdateState(state);
|
||||||
|
|
||||||
|
if (state is not GeneralStationRecordConsoleState cast)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_window?.UpdateState(cast);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
|
||||||
|
_window?.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<DefaultWindow xmlns="https://spacestation14.io"
|
||||||
|
Title="{Loc 'general-station-record-console-window-title'}"
|
||||||
|
MinSize="750 500">
|
||||||
|
<BoxContainer>
|
||||||
|
<!-- Record listing -->
|
||||||
|
<BoxContainer Orientation="Vertical" Margin="5 5 5 5" MinWidth="250" VerticalExpand="True">
|
||||||
|
<Label Name="RecordListingStatus" Visible="False" />
|
||||||
|
<ScrollContainer VerticalExpand="True">
|
||||||
|
<ItemList Name="RecordListing" />
|
||||||
|
</ScrollContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
<BoxContainer Orientation="Vertical" Margin="5 5 5 5">
|
||||||
|
<Label Name="RecordContainerStatus" Visible="False" Text="{Loc 'general-station-record-console-select-record-info'}"/>
|
||||||
|
<BoxContainer Name="RecordContainer" Orientation="Vertical" />
|
||||||
|
</BoxContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
</DefaultWindow>
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Content.Shared.StationRecords;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
|
namespace Content.Client.StationRecords;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
||||||
|
{
|
||||||
|
public Action<StationRecordKey?>? OnKeySelected;
|
||||||
|
private bool _isPopulating;
|
||||||
|
|
||||||
|
public GeneralStationRecordConsoleWindow()
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
|
||||||
|
RecordListing.OnItemSelected += args =>
|
||||||
|
{
|
||||||
|
if (_isPopulating || RecordListing[args.ItemIndex].Metadata is not StationRecordKey cast)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnKeySelected?.Invoke(cast);
|
||||||
|
};
|
||||||
|
|
||||||
|
RecordListing.OnItemDeselected += _ =>
|
||||||
|
{
|
||||||
|
if (!_isPopulating)
|
||||||
|
OnKeySelected?.Invoke(null);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateState(GeneralStationRecordConsoleState state)
|
||||||
|
{
|
||||||
|
if (state.RecordListing == null)
|
||||||
|
{
|
||||||
|
RecordListingStatus.Visible = true;
|
||||||
|
RecordListing.Visible = false;
|
||||||
|
RecordListingStatus.Text = Loc.GetString("general-station-record-console-empty-state");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordListingStatus.Visible = false;
|
||||||
|
RecordListing.Visible = true;
|
||||||
|
PopulateRecordListing(state.RecordListing!, state.SelectedKey);
|
||||||
|
|
||||||
|
RecordContainerStatus.Visible = state.Record == null;
|
||||||
|
|
||||||
|
if (state.Record != null)
|
||||||
|
{
|
||||||
|
RecordContainerStatus.Visible = state.SelectedKey == null;
|
||||||
|
RecordContainerStatus.Text = state.SelectedKey == null
|
||||||
|
? Loc.GetString("general-station-record-console-no-record-found")
|
||||||
|
: Loc.GetString("general-station-record-console-select-record-info");
|
||||||
|
PopulateRecordContainer(state.Record);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RecordContainer.DisposeAllChildren();
|
||||||
|
RecordContainer.RemoveAllChildren();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PopulateRecordListing(Dictionary<StationRecordKey, string> listing, StationRecordKey? selected)
|
||||||
|
{
|
||||||
|
RecordListing.Clear();
|
||||||
|
RecordListing.ClearSelected();
|
||||||
|
|
||||||
|
_isPopulating = true;
|
||||||
|
foreach (var (key, name) in listing)
|
||||||
|
{
|
||||||
|
var item = RecordListing.AddItem(name);
|
||||||
|
item.Metadata = key;
|
||||||
|
|
||||||
|
if (selected != null && key.ID == selected.Value.ID)
|
||||||
|
{
|
||||||
|
item.Selected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_isPopulating = false;
|
||||||
|
|
||||||
|
RecordListing.SortItemsByText();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PopulateRecordContainer(GeneralStationRecord record)
|
||||||
|
{
|
||||||
|
RecordContainer.DisposeAllChildren();
|
||||||
|
RecordContainer.RemoveAllChildren();
|
||||||
|
// sure
|
||||||
|
var recordControls = new Control[]
|
||||||
|
{
|
||||||
|
new Label()
|
||||||
|
{
|
||||||
|
Text = record.Name,
|
||||||
|
StyleClasses = { "LabelBig" }
|
||||||
|
},
|
||||||
|
new Label()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("general-station-record-console-record-age", ("age", record.Age.ToString()))
|
||||||
|
|
||||||
|
},
|
||||||
|
new Label()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("general-station-record-console-record-title", ("job", Loc.GetString(record.JobTitle)))
|
||||||
|
},
|
||||||
|
new Label()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("general-station-record-console-record-species", ("species", record.Species))
|
||||||
|
},
|
||||||
|
new Label()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("general-station-record-console-record-gender", ("gender", record.Gender.ToString()))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var control in recordControls)
|
||||||
|
{
|
||||||
|
RecordContainer.AddChild(control);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Server.Access.Systems;
|
using Content.Server.Access.Systems;
|
||||||
using Content.Server.Administration.Logs;
|
using Content.Server.Station.Systems;
|
||||||
|
using Content.Server.StationRecords;
|
||||||
using Content.Server.UserInterface;
|
using Content.Server.UserInterface;
|
||||||
using Content.Shared.Access.Components;
|
using Content.Shared.Access.Components;
|
||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
using Content.Shared.Containers.ItemSlots;
|
using Content.Shared.StationRecords;
|
||||||
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
|
|
||||||
@@ -91,6 +93,25 @@ namespace Content.Server.Access.Components
|
|||||||
_adminLogger.Add(LogType.Action, LogImpact.Medium,
|
_adminLogger.Add(LogType.Action, LogImpact.Medium,
|
||||||
$"{_entities.ToPrettyString(player):player} has modified {_entities.ToPrettyString(targetIdEntity):entity} with the following accesses: [{string.Join(", ", newAccessList)}]");
|
$"{_entities.ToPrettyString(player):player} has modified {_entities.ToPrettyString(targetIdEntity):entity} with the following accesses: [{string.Join(", ", newAccessList)}]");
|
||||||
|
|
||||||
|
UpdateStationRecord(targetIdEntity, newFullName, newJobTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStationRecord(EntityUid idCard, string newFullName, string newJobTitle)
|
||||||
|
{
|
||||||
|
var station = EntitySystem.Get<StationSystem>().GetOwningStation(Owner);
|
||||||
|
var recordSystem = EntitySystem.Get<StationRecordsSystem>();
|
||||||
|
if (station == null
|
||||||
|
|| !_entities.TryGetComponent(idCard, out StationRecordKeyStorageComponent? keyStorage)
|
||||||
|
|| keyStorage.Key == null
|
||||||
|
|| !recordSystem.TryGetRecord(station.Value, keyStorage.Key.Value, out GeneralStationRecord? record))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
record.Name = newFullName;
|
||||||
|
record.JobTitle = newJobTitle;
|
||||||
|
|
||||||
|
recordSystem.Synchronize(station.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateUserInterface()
|
public void UpdateUserInterface()
|
||||||
|
|||||||
47
Content.Server/CrewManifest/CrewManifestEui.cs
Normal file
47
Content.Server/CrewManifest/CrewManifestEui.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using Content.Server.EUI;
|
||||||
|
using Content.Shared.CrewManifest;
|
||||||
|
using Content.Shared.Eui;
|
||||||
|
|
||||||
|
namespace Content.Server.CrewManifest;
|
||||||
|
|
||||||
|
public sealed class CrewManifestEui : BaseEui
|
||||||
|
{
|
||||||
|
private readonly CrewManifestSystem _crewManifest;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Station this EUI instance is currently tracking.
|
||||||
|
/// </summary>
|
||||||
|
private readonly EntityUid _station;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current owner of this UI, if it has one. This is
|
||||||
|
/// to ensure that if a BUI is closed, the EUIs related
|
||||||
|
/// to the BUI are closed as well.
|
||||||
|
/// </summary>
|
||||||
|
public readonly EntityUid? Owner;
|
||||||
|
|
||||||
|
public CrewManifestEui(EntityUid station, EntityUid? owner, CrewManifestSystem crewManifestSystem)
|
||||||
|
{
|
||||||
|
_station = station;
|
||||||
|
Owner = owner;
|
||||||
|
_crewManifest = crewManifestSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override CrewManifestEuiState GetNewState()
|
||||||
|
{
|
||||||
|
var (name, entries) = _crewManifest.GetCrewManifest(_station);
|
||||||
|
return new(name, entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleMessage(EuiMessageBase msg)
|
||||||
|
{
|
||||||
|
base.HandleMessage(msg);
|
||||||
|
|
||||||
|
switch (msg)
|
||||||
|
{
|
||||||
|
case CrewManifestEuiClosed:
|
||||||
|
_crewManifest.CloseEui(_station, Player, Owner);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
263
Content.Server/CrewManifest/CrewManifestSystem.cs
Normal file
263
Content.Server/CrewManifest/CrewManifestSystem.cs
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
using Content.Server.Administration;
|
||||||
|
using Content.Server.EUI;
|
||||||
|
using Content.Server.GameTicking;
|
||||||
|
using Content.Server.Station.Systems;
|
||||||
|
using Content.Server.StationRecords;
|
||||||
|
using Content.Shared.Administration;
|
||||||
|
using Content.Shared.CCVar;
|
||||||
|
using Content.Shared.CrewManifest;
|
||||||
|
using Content.Shared.GameTicking;
|
||||||
|
using Content.Shared.Roles;
|
||||||
|
using Content.Shared.StationRecords;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Server.Player;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.Console;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Players;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Server.CrewManifest;
|
||||||
|
|
||||||
|
public sealed class CrewManifestSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly StationSystem _stationSystem = default!;
|
||||||
|
[Dependency] private readonly StationRecordsSystem _recordsSystem = default!;
|
||||||
|
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||||
|
[Dependency] private readonly EuiManager _euiManager = default!;
|
||||||
|
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cached crew manifest entries. The alternative is to outright
|
||||||
|
/// rebuild the crew manifest every time the state is requested:
|
||||||
|
/// this is inefficient.
|
||||||
|
/// </summary>
|
||||||
|
private readonly Dictionary<EntityUid, CrewManifestEntries> _cachedEntries = new();
|
||||||
|
|
||||||
|
private readonly Dictionary<EntityUid, Dictionary<IPlayerSession, CrewManifestEui>> _openEuis = new();
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<AfterGeneralRecordCreatedEvent>(AfterGeneralRecordCreated);
|
||||||
|
SubscribeLocalEvent<RecordModifiedEvent>(OnRecordModified);
|
||||||
|
SubscribeLocalEvent<CrewManifestViewerComponent, BoundUIClosedEvent>(OnBoundUiClose);
|
||||||
|
SubscribeLocalEvent<CrewManifestViewerComponent, CrewManifestOpenUiMessage>(OpenEuiFromBui);
|
||||||
|
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
|
||||||
|
SubscribeNetworkEvent<RequestCrewManifestMessage>(OnRequestCrewManifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRoundRestart(RoundRestartCleanupEvent ev)
|
||||||
|
{
|
||||||
|
foreach (var (_, euis) in _openEuis)
|
||||||
|
{
|
||||||
|
foreach (var (_, eui) in euis)
|
||||||
|
{
|
||||||
|
eui.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_openEuis.Clear();
|
||||||
|
_cachedEntries.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRequestCrewManifest(RequestCrewManifestMessage message, EntitySessionEventArgs args)
|
||||||
|
{
|
||||||
|
if (args.SenderSession is not IPlayerSession sessionCast
|
||||||
|
|| !_configManager.GetCVar(CCVars.CrewManifestWithoutEntity))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenEui(message.Id, sessionCast);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not a big fan of this one. Rebuilds the crew manifest every time
|
||||||
|
// somebody spawns in, meaning that at round start, it rebuilds the crew manifest
|
||||||
|
// wrt the amount of players readied up.
|
||||||
|
private void AfterGeneralRecordCreated(AfterGeneralRecordCreatedEvent ev)
|
||||||
|
{
|
||||||
|
BuildCrewManifest(ev.Key.OriginStation);
|
||||||
|
UpdateEuis(ev.Key.OriginStation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRecordModified(RecordModifiedEvent ev)
|
||||||
|
{
|
||||||
|
BuildCrewManifest(ev.Key.OriginStation);
|
||||||
|
UpdateEuis(ev.Key.OriginStation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBoundUiClose(EntityUid uid, CrewManifestViewerComponent component, BoundUIClosedEvent ev)
|
||||||
|
{
|
||||||
|
var owningStation = _stationSystem.GetOwningStation(uid);
|
||||||
|
if (owningStation == null || ev.Session is not IPlayerSession sessionCast)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseEui(owningStation.Value, sessionCast, uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the crew manifest for a given station, along with the name of the station.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="station">Entity uid of the station.</param>
|
||||||
|
/// <returns>The name and crew manifest entries (unordered) of the station.</returns>
|
||||||
|
public (string name, CrewManifestEntries? entries) GetCrewManifest(EntityUid station)
|
||||||
|
{
|
||||||
|
var valid = _cachedEntries.TryGetValue(station, out var manifest);
|
||||||
|
return (valid ? MetaData(station).EntityName : string.Empty, valid ? manifest : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateEuis(EntityUid station)
|
||||||
|
{
|
||||||
|
if (_openEuis.TryGetValue(station, out var euis))
|
||||||
|
{
|
||||||
|
foreach (var eui in euis.Values)
|
||||||
|
{
|
||||||
|
eui.StateDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenEuiFromBui(EntityUid uid, CrewManifestViewerComponent component, CrewManifestOpenUiMessage msg)
|
||||||
|
{
|
||||||
|
var owningStation = _stationSystem.GetOwningStation(uid);
|
||||||
|
if (owningStation == null || msg.Session is not IPlayerSession sessionCast)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_configManager.GetCVar(CCVars.CrewManifestUnsecure) && component.Unsecure)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenEui(owningStation.Value, sessionCast, uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens a crew manifest EUI for a given player.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="station">Station that we're displaying the crew manifest for.</param>
|
||||||
|
/// <param name="session">The player's session.</param>
|
||||||
|
/// <param name="owner">If this EUI should be 'owned' by an entity.</param>
|
||||||
|
public void OpenEui(EntityUid station, IPlayerSession session, EntityUid? owner = null)
|
||||||
|
{
|
||||||
|
if (!HasComp<StationRecordsComponent>(station))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_openEuis.TryGetValue(station, out var euis))
|
||||||
|
{
|
||||||
|
euis = new();
|
||||||
|
_openEuis.Add(station, euis);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (euis.ContainsKey(session))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var eui = new CrewManifestEui(station, owner, this);
|
||||||
|
euis.Add(session, eui);
|
||||||
|
|
||||||
|
_euiManager.OpenEui(eui, session);
|
||||||
|
eui.StateDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes an EUI for a given player.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="station">Station that we're displaying the crew manifest for.</param>
|
||||||
|
/// <param name="session">The player's session.</param>
|
||||||
|
/// <param name="owner">The owner of this EUI, if there was one.</param>
|
||||||
|
public void CloseEui(EntityUid station, IPlayerSession session, EntityUid? owner = null)
|
||||||
|
{
|
||||||
|
if (!HasComp<StationRecordsComponent>(station))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_openEuis.TryGetValue(station, out var euis)
|
||||||
|
|| !euis.TryGetValue(session, out var eui))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eui.Owner == owner)
|
||||||
|
{
|
||||||
|
eui.Close();
|
||||||
|
euis.Remove(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (euis.Count == 0)
|
||||||
|
{
|
||||||
|
_openEuis.Remove(station);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the crew manifest for a station. Stores it in the cache afterwards.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="station"></param>
|
||||||
|
private void BuildCrewManifest(EntityUid station)
|
||||||
|
{
|
||||||
|
var iter = _recordsSystem.GetRecordsOfType<GeneralStationRecord>(station);
|
||||||
|
|
||||||
|
var entries = new CrewManifestEntries();
|
||||||
|
|
||||||
|
foreach (var recordObject in iter)
|
||||||
|
{
|
||||||
|
var record = recordObject.Item2;
|
||||||
|
var entry = new CrewManifestEntry(record.Name, record.JobTitle, record.JobIcon, record.JobPrototype);
|
||||||
|
|
||||||
|
entries.Entries.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cachedEntries.ContainsKey(station))
|
||||||
|
{
|
||||||
|
_cachedEntries[station] = entries;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_cachedEntries.Add(station, entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[AdminCommand(AdminFlags.Admin)]
|
||||||
|
public sealed class CrewManifestCommand : IConsoleCommand
|
||||||
|
{
|
||||||
|
public string Command => "crewmanifest";
|
||||||
|
public string Description => "Opens the crew manifest for the given station.";
|
||||||
|
public string Help => $"Usage: {Command} <entity uid>";
|
||||||
|
|
||||||
|
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length != 1)
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Invalid argument count.\n{Help}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||||
|
|
||||||
|
if (!EntityUid.TryParse(args[0], out var uid))
|
||||||
|
{
|
||||||
|
shell.WriteLine($"{args[0]} is not a valid entity UID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shell.Player == null || shell.Player is not IPlayerSession session)
|
||||||
|
{
|
||||||
|
shell.WriteLine("You must run this from a client.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var crewManifestSystem = entMan.EntitySysManager.GetEntitySystem<CrewManifestSystem>();
|
||||||
|
|
||||||
|
crewManifestSystem.OpenEui(uid, session);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
Content.Server/CrewManifest/CrewManifestViewerComponent.cs
Normal file
12
Content.Server/CrewManifest/CrewManifestViewerComponent.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Content.Server.CrewManifest;
|
||||||
|
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed class CrewManifestViewerComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// If this manifest viewer is unsecure or not. If it is,
|
||||||
|
/// CCVars.CrewManifestUnsecure being false will
|
||||||
|
/// not allow this entity to be processed by CrewManifestSystem.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("unsecure")] public bool Unsecure;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using Content.Shared.StationRecords;
|
||||||
|
|
||||||
|
namespace Content.Server.StationRecords;
|
||||||
|
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed class GeneralStationRecordConsoleComponent : Component
|
||||||
|
{
|
||||||
|
public StationRecordKey? ActiveKey { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using Content.Shared.StationRecords;
|
||||||
|
|
||||||
|
namespace Content.Server.StationRecords;
|
||||||
|
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed class StationRecordKeyStorageComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The key stored in this component.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public StationRecordKey? Key;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace Content.Server.StationRecords;
|
||||||
|
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed class StationRecordsComponent : Component
|
||||||
|
{
|
||||||
|
// Every single record in this station, by key.
|
||||||
|
// Essentially a columnar database, but I really suck
|
||||||
|
// at implementing that so
|
||||||
|
[ViewVariables]
|
||||||
|
public StationRecordSet Records = new();
|
||||||
|
}
|
||||||
158
Content.Server/StationRecords/StationRecordSet.cs
Normal file
158
Content.Server/StationRecords/StationRecordSet.cs
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Content.Shared.StationRecords;
|
||||||
|
|
||||||
|
namespace Content.Server.StationRecords;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set of station records. StationRecordsComponent stores these.
|
||||||
|
/// Keyed by StationRecordKey, which should be obtained from
|
||||||
|
/// an entity that stores a reference to it.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class StationRecordSet
|
||||||
|
{
|
||||||
|
private uint _currentRecordId;
|
||||||
|
|
||||||
|
private HashSet<StationRecordKey> _keys = new();
|
||||||
|
|
||||||
|
private HashSet<StationRecordKey> _recentlyAccessed = new();
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
private Dictionary<Type, Dictionary<StationRecordKey, object>> _tables = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all records of a specific type stored in the record set.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of record to fetch.</typeparam>
|
||||||
|
/// <returns>An enumerable object that contains a pair of both a station key, and the record associated with it.</returns>
|
||||||
|
public IEnumerable<(StationRecordKey, T)> GetRecordsOfType<T>()
|
||||||
|
{
|
||||||
|
if (!_tables.ContainsKey(typeof(T)))
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (key, entry) in _tables[typeof(T)])
|
||||||
|
{
|
||||||
|
if (entry is not T cast)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_recentlyAccessed.Add(key);
|
||||||
|
|
||||||
|
yield return (key, cast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a new record into this set of entries.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="station">Station that we're adding the record for.</param>
|
||||||
|
/// <returns>A key that represents the record in this set.</returns>
|
||||||
|
public StationRecordKey AddRecord(EntityUid station)
|
||||||
|
{
|
||||||
|
var key = new StationRecordKey(_currentRecordId++, station);
|
||||||
|
|
||||||
|
_keys.Add(key);
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add an entry into a record.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Key for the record.</param>
|
||||||
|
/// <param name="entry">Entry to add.</param>
|
||||||
|
/// <typeparam name="T">Type of the entry that's being added.</typeparam>
|
||||||
|
public void AddRecordEntry<T>(StationRecordKey key, T entry)
|
||||||
|
{
|
||||||
|
if (!_keys.Contains(key) || entry == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_tables.TryGetValue(typeof(T), out var table))
|
||||||
|
{
|
||||||
|
table = new();
|
||||||
|
_tables.Add(typeof(T), table);
|
||||||
|
}
|
||||||
|
|
||||||
|
table.Add(key, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try to get an record entry by type, from this record key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The StationRecordKey to get the entries from.</param>
|
||||||
|
/// <param name="entry">The entry that is retrieved from the record set.</param>
|
||||||
|
/// <typeparam name="T">The type of entry to search for.</typeparam>
|
||||||
|
/// <returns>True if the record exists and was retrieved, false otherwise.</returns>
|
||||||
|
public bool TryGetRecordEntry<T>(StationRecordKey key, [NotNullWhen(true)] out T? entry)
|
||||||
|
{
|
||||||
|
entry = default;
|
||||||
|
|
||||||
|
if (!_keys.Contains(key)
|
||||||
|
|| !_tables.TryGetValue(typeof(T), out var table)
|
||||||
|
|| !table.TryGetValue(key, out var entryObject))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = (T) entryObject;
|
||||||
|
_recentlyAccessed.Add(key);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the record associated with this key has an entry of a certain type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The record key.</param>
|
||||||
|
/// <typeparam name="T">Type to check.</typeparam>
|
||||||
|
/// <returns>True if the entry exists, false otherwise.</returns>
|
||||||
|
public bool HasRecordEntry<T>(StationRecordKey key)
|
||||||
|
{
|
||||||
|
return _keys.Contains(key)
|
||||||
|
&& _tables.TryGetValue(typeof(T), out var table)
|
||||||
|
&& table.ContainsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the recently accessed keys from this record set.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>All recently accessed keys from this record set.</returns>
|
||||||
|
public IEnumerable<StationRecordKey> GetRecentlyAccessed()
|
||||||
|
{
|
||||||
|
return _recentlyAccessed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears the recently accessed keys from the set.
|
||||||
|
/// </summary>
|
||||||
|
public void ClearRecentlyAccessed()
|
||||||
|
{
|
||||||
|
_recentlyAccessed.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all record entries related to this key from this set.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key to remove.</param>
|
||||||
|
/// <returns>True if successful, false otherwise.</returns>
|
||||||
|
public bool RemoveAllRecords(StationRecordKey key)
|
||||||
|
{
|
||||||
|
if (!_keys.Remove(key))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var table in _tables.Values)
|
||||||
|
{
|
||||||
|
table.Remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
using Content.Server.Station.Systems;
|
||||||
|
using Content.Shared.StationRecords;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.StationRecords.Systems;
|
||||||
|
|
||||||
|
public sealed class GeneralStationRecordConsoleSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
|
||||||
|
[Dependency] private readonly StationSystem _stationSystem = default!;
|
||||||
|
[Dependency] private readonly StationRecordsSystem _stationRecordsSystem = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<GeneralStationRecordConsoleComponent, BoundUIOpenedEvent>(UpdateUserInterface);
|
||||||
|
SubscribeLocalEvent<GeneralStationRecordConsoleComponent, SelectGeneralStationRecord>(OnKeySelected);
|
||||||
|
SubscribeLocalEvent<GeneralStationRecordConsoleComponent, RecordModifiedEvent>(UpdateUserInterface);
|
||||||
|
SubscribeLocalEvent<GeneralStationRecordConsoleComponent, AfterGeneralRecordCreatedEvent>(UpdateUserInterface);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateUserInterface<T>(EntityUid uid, GeneralStationRecordConsoleComponent component, T ev)
|
||||||
|
{
|
||||||
|
UpdateUserInterface(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnKeySelected(EntityUid uid, GeneralStationRecordConsoleComponent component,
|
||||||
|
SelectGeneralStationRecord msg)
|
||||||
|
{
|
||||||
|
component.ActiveKey = msg.SelectedKey;
|
||||||
|
UpdateUserInterface(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateUserInterface(EntityUid uid, GeneralStationRecordConsoleComponent? console = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref console))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var owningStation = _stationSystem.GetOwningStation(uid);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (!TryComp<StationRecordsComponent>(owningStation, out var stationRecordsComponent))
|
||||||
|
{
|
||||||
|
_userInterface.GetUiOrNull(uid, GeneralStationRecordConsoleKey.Key)?.SetState(new GeneralStationRecordConsoleState(null, null, null));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var enumerator = _stationRecordsSystem.GetRecordsOfType<GeneralStationRecord>(owningStation.Value, stationRecordsComponent);
|
||||||
|
|
||||||
|
var listing = new Dictionary<StationRecordKey, string>();
|
||||||
|
foreach (var pair in enumerator)
|
||||||
|
{
|
||||||
|
listing.Add(pair.Item1, pair.Item2.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listing.Count == 0)
|
||||||
|
{
|
||||||
|
_userInterface.GetUiOrNull(uid, GeneralStationRecordConsoleKey.Key)?.SetState(new GeneralStationRecordConsoleState(null, null, null));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GeneralStationRecord? record = null;
|
||||||
|
if (console.ActiveKey != null)
|
||||||
|
{
|
||||||
|
_stationRecordsSystem.TryGetRecord(owningStation.Value, console.ActiveKey.Value, out record,
|
||||||
|
stationRecordsComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
_userInterface
|
||||||
|
.GetUiOrNull(uid, GeneralStationRecordConsoleKey.Key)?
|
||||||
|
.SetState(new GeneralStationRecordConsoleState(console.ActiveKey, record, listing));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
using Content.Shared.StationRecords;
|
||||||
|
|
||||||
|
namespace Content.Server.StationRecords.Systems;
|
||||||
|
|
||||||
|
public sealed class StationRecordKeyStorageSystem : EntitySystem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Assigns a station record key to an entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid"></param>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="keyStorage"></param>
|
||||||
|
public void AssignKey(EntityUid uid, StationRecordKey key, StationRecordKeyStorageComponent? keyStorage = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref keyStorage))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
keyStorage.Key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a station record key from an entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid"></param>
|
||||||
|
/// <param name="keyStorage"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public StationRecordKey? RemoveKey(EntityUid uid, StationRecordKeyStorageComponent? keyStorage = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref keyStorage) || keyStorage.Key == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = keyStorage.Key;
|
||||||
|
keyStorage.Key = null;
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if an entity currently contains a station record key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid"></param>
|
||||||
|
/// <param name="keyStorage"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool CheckKey(EntityUid uid, StationRecordKeyStorageComponent? keyStorage = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref keyStorage))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyStorage.Key != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
282
Content.Server/StationRecords/Systems/StationRecordsSystem.cs
Normal file
282
Content.Server/StationRecords/Systems/StationRecordsSystem.cs
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Content.Server.Access.Systems;
|
||||||
|
using Content.Server.GameTicking;
|
||||||
|
using Content.Server.Station.Systems;
|
||||||
|
using Content.Server.StationRecords;
|
||||||
|
using Content.Server.StationRecords.Systems;
|
||||||
|
using Content.Shared.Access.Components;
|
||||||
|
using Content.Shared.Inventory;
|
||||||
|
using Content.Shared.PDA;
|
||||||
|
using Content.Shared.Preferences;
|
||||||
|
using Content.Shared.Roles;
|
||||||
|
using Content.Shared.StationRecords;
|
||||||
|
using Robust.Shared.Enums;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Station records.
|
||||||
|
///
|
||||||
|
/// A station record is tied to an ID card, or anything that holds
|
||||||
|
/// a station record's key. This key will determine access to a
|
||||||
|
/// station record set's record entries, and it is imperative not
|
||||||
|
/// to lose the item that holds the key under any circumstance.
|
||||||
|
///
|
||||||
|
/// Records are mostly a roleplaying tool, but can have some
|
||||||
|
/// functionality as well (i.e., security records indicating that
|
||||||
|
/// a specific person holding an ID card with a linked key is
|
||||||
|
/// currently under warrant, showing a crew manifest with user
|
||||||
|
/// settable, custom titles).
|
||||||
|
///
|
||||||
|
/// General records are tied into this system, as most crewmembers
|
||||||
|
/// should have a general record - and most systems should probably
|
||||||
|
/// depend on this general record being created. This is subject
|
||||||
|
/// to change.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class StationRecordsSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||||
|
[Dependency] private readonly StationRecordKeyStorageSystem _keyStorageSystem = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<StationInitializedEvent>(OnStationInitialize);
|
||||||
|
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnPlayerSpawn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStationInitialize(StationInitializedEvent args)
|
||||||
|
{
|
||||||
|
AddComp<StationRecordsComponent>(args.Station);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPlayerSpawn(PlayerSpawnCompleteEvent args)
|
||||||
|
{
|
||||||
|
CreateGeneralRecord(args.Station, args.Mob, args.Profile, args.JobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateGeneralRecord(EntityUid station, EntityUid player, HumanoidCharacterProfile profile,
|
||||||
|
string? jobId, StationRecordsComponent? records = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(station, ref records)
|
||||||
|
|| String.IsNullOrEmpty(jobId)
|
||||||
|
|| !_prototypeManager.HasIndex<JobPrototype>(jobId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_inventorySystem.TryGetSlotEntity(player, "id", out var idUid))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateGeneralRecord(station, idUid.Value, profile.Name, profile.Age, profile.Species, profile.Gender, jobId, profile, records);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a general record to store in a station's record set.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is tied into the record system, as any crew member's
|
||||||
|
/// records should generally be dependent on some generic
|
||||||
|
/// record with the bare minimum of information involved.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="station">The entity uid of the station.</param>
|
||||||
|
/// <param name="idUid">The entity uid of an entity's ID card. Can be null.</param>
|
||||||
|
/// <param name="name">Name of the character.</param>
|
||||||
|
/// <param name="species">Species of the character.</param>
|
||||||
|
/// <param name="gender">Gender of the character.</param>
|
||||||
|
/// <param name="jobId">
|
||||||
|
/// The job to initially tie this record to. This must be a valid job loaded in, otherwise
|
||||||
|
/// this call will cause an exception. Ensure that a general record starts out with a job
|
||||||
|
/// that is currently a valid job prototype.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="profile">
|
||||||
|
/// Profile for the related player. This is so that other systems can get further information
|
||||||
|
/// about the player character.
|
||||||
|
/// Optional - other systems should anticipate this.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="records">Station records component.</param>
|
||||||
|
public void CreateGeneralRecord(EntityUid station, EntityUid? idUid, string name, int age, string species, Gender gender, string jobId, HumanoidCharacterProfile? profile = null,
|
||||||
|
StationRecordsComponent? records = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(station, ref records))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_prototypeManager.TryIndex(jobId, out JobPrototype? jobPrototype))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Invalid job prototype ID: {jobId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var record = new GeneralStationRecord()
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
Age = age,
|
||||||
|
JobTitle = jobPrototype.Name,
|
||||||
|
JobIcon = jobPrototype.Icon,
|
||||||
|
JobPrototype = jobId,
|
||||||
|
Species = species,
|
||||||
|
Gender = gender,
|
||||||
|
DisplayPriority = jobPrototype.Weight
|
||||||
|
};
|
||||||
|
|
||||||
|
var key = records.Records.AddRecord(station);
|
||||||
|
records.Records.AddRecordEntry(key, record);
|
||||||
|
// entry.Entries.Add(typeof(GeneralStationRecord), record);
|
||||||
|
|
||||||
|
if (idUid != null)
|
||||||
|
{
|
||||||
|
var keyStorageEntity = idUid;
|
||||||
|
if (TryComp(idUid, out PDAComponent? pdaComponent) && pdaComponent.ContainedID != null)
|
||||||
|
{
|
||||||
|
keyStorageEntity = pdaComponent.IdSlot.Item;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyStorageEntity != null)
|
||||||
|
{
|
||||||
|
_keyStorageSystem.AssignKey(keyStorageEntity.Value, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RaiseLocalEvent(new AfterGeneralRecordCreatedEvent(key, record, profile));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a record from this station.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="station">Station to remove the record from.</param>
|
||||||
|
/// <param name="key">The key to remove.</param>
|
||||||
|
/// <param name="records">Station records component.</param>
|
||||||
|
/// <returns>True if the record was removed, false otherwise.</returns>
|
||||||
|
public bool RemoveRecord(EntityUid station, StationRecordKey key, StationRecordsComponent? records = null)
|
||||||
|
{
|
||||||
|
if (station != key.OriginStation || !Resolve(station, ref records))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RaiseLocalEvent(new RecordRemovedEvent(key));
|
||||||
|
|
||||||
|
return records.Records.RemoveAllRecords(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try to get a record from this station's record entries,
|
||||||
|
/// from the provided station record key. Will always return
|
||||||
|
/// null if the key does not match the station.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="station">Station to get the record from.</param>
|
||||||
|
/// <param name="key">Key to try and index from the record set.</param>
|
||||||
|
/// <param name="entry">The resulting entry.</param>
|
||||||
|
/// <param name="records">Station record component.</param>
|
||||||
|
/// <typeparam name="T">Type to get from the record set.</typeparam>
|
||||||
|
/// <returns>True if the record was obtained, false otherwise.</returns>
|
||||||
|
public bool TryGetRecord<T>(EntityUid station, StationRecordKey key, [NotNullWhen(true)] out T? entry, StationRecordsComponent? records = null)
|
||||||
|
{
|
||||||
|
entry = default;
|
||||||
|
|
||||||
|
if (key.OriginStation != station || !Resolve(station, ref records))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return records.Records.TryGetRecordEntry(key, out entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all records of a specific type from a station.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="station">The station to get the records from.</param>
|
||||||
|
/// <param name="records">Station records component.</param>
|
||||||
|
/// <typeparam name="T">Type of record to fetch</typeparam>
|
||||||
|
/// <returns>Enumerable of pairs with a station record key, and the entry in question of type T.</returns>
|
||||||
|
public IEnumerable<(StationRecordKey, T)> GetRecordsOfType<T>(EntityUid station, StationRecordsComponent? records = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(station, ref records))
|
||||||
|
{
|
||||||
|
return new (StationRecordKey, T)[]{};
|
||||||
|
}
|
||||||
|
|
||||||
|
return records.Records.GetRecordsOfType<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Synchronizes a station's records with any systems that need it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="station">The station to synchronize any recently accessed records with..</param>
|
||||||
|
/// <param name="records">Station records component.</param>
|
||||||
|
public void Synchronize(EntityUid station, StationRecordsComponent? records = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(station, ref records))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var key in records.Records.GetRecentlyAccessed())
|
||||||
|
{
|
||||||
|
RaiseLocalEvent(new RecordModifiedEvent(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
records.Records.ClearRecentlyAccessed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event raised after the player's general profile is created.
|
||||||
|
/// Systems that modify records on a station would have more use
|
||||||
|
/// listening to this event, as it contains the character's record key.
|
||||||
|
/// Also stores the general record reference, to save some time.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class AfterGeneralRecordCreatedEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public StationRecordKey Key { get; }
|
||||||
|
public GeneralStationRecord Record { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// Profile for the related player. This is so that other systems can get further information
|
||||||
|
/// about the player character.
|
||||||
|
/// Optional - other systems should anticipate this.
|
||||||
|
/// </summary>
|
||||||
|
public HumanoidCharacterProfile? Profile { get; }
|
||||||
|
|
||||||
|
public AfterGeneralRecordCreatedEvent(StationRecordKey key, GeneralStationRecord record, HumanoidCharacterProfile? profile)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
Record = record;
|
||||||
|
Profile = profile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event raised after a record is removed. Only the key is given
|
||||||
|
/// when the record is removed, so that any relevant systems/components
|
||||||
|
/// that store record keys can then remove the key from their internal
|
||||||
|
/// fields.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class RecordRemovedEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public StationRecordKey Key { get; }
|
||||||
|
|
||||||
|
public RecordRemovedEvent(StationRecordKey key)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event raised after a record is modified. This is to
|
||||||
|
/// inform other systems that records stored in this key
|
||||||
|
/// may have changed.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class RecordModifiedEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public StationRecordKey Key { get; }
|
||||||
|
|
||||||
|
public RecordModifiedEvent(StationRecordKey key)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -955,6 +955,37 @@ namespace Content.Shared.CCVar
|
|||||||
public static readonly CVarDef<string> CentcommMap =
|
public static readonly CVarDef<string> CentcommMap =
|
||||||
CVarDef.Create("shuttle.centcomm_map", "/Maps/centcomm.yml", CVar.SERVERONLY);
|
CVarDef.Create("shuttle.centcomm_map", "/Maps/centcomm.yml", CVar.SERVERONLY);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Crew Manifests
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Setting this allows a crew manifest to be opened from any window
|
||||||
|
/// that has a crew manifest button, and sends the correct message.
|
||||||
|
/// If this is false, only in-game entities will allow you to see
|
||||||
|
/// the crew manifest, if the functionality is coded in.
|
||||||
|
/// Having administrator priveledge ignores this, but will still
|
||||||
|
/// hide the button in UI windows.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly CVarDef<bool> CrewManifestWithoutEntity =
|
||||||
|
CVarDef.Create("crewmanifest.no_entity", true, CVar.REPLICATED);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Setting this allows the crew manifest to be viewed from 'unsecure'
|
||||||
|
/// entities, such as the PDA.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly CVarDef<bool> CrewManifestUnsecure =
|
||||||
|
CVarDef.Create("crewmanifest.unsecure", true, CVar.REPLICATED);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dictates the order the crew manifest will appear in, in terms of its sections.
|
||||||
|
/// Sections not in this list will appear at the end of the list, in no
|
||||||
|
/// specific order.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly CVarDef<string> CrewManifestOrdering =
|
||||||
|
CVarDef.Create("crewmanifest.ordering", "Command,Security,Science,Medical,Engineering,Cargo,Civilian,Unknown",
|
||||||
|
CVar.REPLICATED);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* VIEWPORT
|
* VIEWPORT
|
||||||
*/
|
*/
|
||||||
|
|||||||
75
Content.Shared/CrewManifest/SharedCrewManifestSystem.cs
Normal file
75
Content.Shared/CrewManifest/SharedCrewManifestSystem.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
using Content.Shared.Eui;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.CrewManifest;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A message to send to the server when requesting a crew manifest.
|
||||||
|
/// CrewManifestSystem will open an EUI that will send the crew manifest
|
||||||
|
/// to the player when it is updated.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class RequestCrewManifestMessage : EntityEventArgs
|
||||||
|
{
|
||||||
|
public EntityUid Id { get; }
|
||||||
|
|
||||||
|
public RequestCrewManifestMessage(EntityUid id)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class CrewManifestEuiState : EuiStateBase
|
||||||
|
{
|
||||||
|
public string StationName { get; }
|
||||||
|
public CrewManifestEntries? Entries { get; }
|
||||||
|
|
||||||
|
public CrewManifestEuiState(string stationName, CrewManifestEntries? entries)
|
||||||
|
{
|
||||||
|
StationName = stationName;
|
||||||
|
Entries = entries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class CrewManifestEuiClosed : EuiMessageBase
|
||||||
|
{}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class CrewManifestEntries
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Entries in the crew manifest. Goes by department ID.
|
||||||
|
/// </summary>
|
||||||
|
// public Dictionary<string, List<CrewManifestEntry>> Entries = new();
|
||||||
|
public List<CrewManifestEntry> Entries = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class CrewManifestEntry
|
||||||
|
{
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public string JobTitle { get; }
|
||||||
|
|
||||||
|
public string JobIcon { get; }
|
||||||
|
|
||||||
|
public string JobPrototype { get; }
|
||||||
|
|
||||||
|
public CrewManifestEntry(string name, string jobTitle, string jobIcon, string jobPrototype)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
JobTitle = jobTitle;
|
||||||
|
JobIcon = jobIcon;
|
||||||
|
JobPrototype = jobPrototype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tells the server to open a crew manifest UI from
|
||||||
|
/// this entity's point of view.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class CrewManifestOpenUiMessage : BoundUserInterfaceMessage
|
||||||
|
{}
|
||||||
59
Content.Shared/StationRecords/GeneralStationRecord.cs
Normal file
59
Content.Shared/StationRecords/GeneralStationRecord.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using Robust.Shared.Enums;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.StationRecords;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// General station record. Indicates the crewmember's name and job.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class GeneralStationRecord
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Name tied to this station record.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public string Name = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Age of the person that this station record represents.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public int Age;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Job title tied to this station record.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public string JobTitle = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Job icon tied to this station record.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public string JobIcon = string.Empty;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string JobPrototype = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Species tied to this station record.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public string Species = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gender identity tied to this station record.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Sex should be placed in a medical record, not a general record.</remarks>
|
||||||
|
[ViewVariables]
|
||||||
|
public Gender Gender = Gender.Neuter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The priority to display this record at.
|
||||||
|
/// This is taken from the 'weight' of a job prototype,
|
||||||
|
/// usually.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public int DisplayPriority;
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.StationRecords;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum GeneralStationRecordConsoleKey : byte
|
||||||
|
{
|
||||||
|
Key
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// General station records console state. There are a few states:
|
||||||
|
/// - SelectedKey null, Record null, RecordListing null
|
||||||
|
/// - The station record database could not be accessed.
|
||||||
|
/// - SelectedKey null, Record null, RecordListing non-null
|
||||||
|
/// - Records are populated in the database, or at least the station has
|
||||||
|
/// the correct component.
|
||||||
|
/// - SelectedKey non-null, Record null, RecordListing non-null
|
||||||
|
/// - The selected key does not have a record tied to it.
|
||||||
|
/// - SelectedKey non-null, Record non-null, RecordListing non-null
|
||||||
|
/// - The selected key has a record tied to it, and the record has been sent.
|
||||||
|
///
|
||||||
|
/// Other states are erroneous.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class GeneralStationRecordConsoleState : BoundUserInterfaceState
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Current selected key.
|
||||||
|
/// </summary>
|
||||||
|
public StationRecordKey? SelectedKey { get; }
|
||||||
|
public GeneralStationRecord? Record { get; }
|
||||||
|
public Dictionary<StationRecordKey, string>? RecordListing { get; }
|
||||||
|
|
||||||
|
public GeneralStationRecordConsoleState(StationRecordKey? key, GeneralStationRecord? record, Dictionary<StationRecordKey, string>? recordListing)
|
||||||
|
{
|
||||||
|
SelectedKey = key;
|
||||||
|
Record = record;
|
||||||
|
RecordListing = recordListing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsEmpty() => SelectedKey == null && Record == null && RecordListing == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class SelectGeneralStationRecord : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public StationRecordKey? SelectedKey { get; }
|
||||||
|
|
||||||
|
public SelectGeneralStationRecord(StationRecordKey? selectedKey)
|
||||||
|
{
|
||||||
|
SelectedKey = selectedKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Content.Shared/StationRecords/StationRecordKey.cs
Normal file
21
Content.Shared/StationRecords/StationRecordKey.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.StationRecords;
|
||||||
|
|
||||||
|
// Station record keys. These should be stored somewhere,
|
||||||
|
// preferably within an ID card.
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public readonly struct StationRecordKey
|
||||||
|
{
|
||||||
|
[ViewVariables]
|
||||||
|
public uint ID { get; }
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public EntityUid OriginStation { get; }
|
||||||
|
|
||||||
|
public StationRecordKey(uint id, EntityUid originStation)
|
||||||
|
{
|
||||||
|
ID = id;
|
||||||
|
OriginStation = originStation;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ id-card-console-window-save-button = Save
|
|||||||
id-card-console-window-job-title-label = Job title:
|
id-card-console-window-job-title-label = Job title:
|
||||||
id-card-console-window-eject-button = Eject
|
id-card-console-window-eject-button = Eject
|
||||||
id-card-console-window-insert-button = Insert
|
id-card-console-window-insert-button = Insert
|
||||||
|
id-card-console-window-job-selection-label = Job presets:
|
||||||
|
|
||||||
access-id-card-console-component-no-hands-error = You have no hands.
|
access-id-card-console-component-no-hands-error = You have no hands.
|
||||||
id-card-console-privileged-id = Privileged ID
|
id-card-console-privileged-id = Privileged ID
|
||||||
|
|||||||
3
Resources/Locale/en-US/crew-manifest/crew-manifest.ftl
Normal file
3
Resources/Locale/en-US/crew-manifest/crew-manifest.ftl
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
crew-manifest-window-title = Crew Manifest
|
||||||
|
crew-manifest-button-label = Crew Manifest
|
||||||
|
crew-manifest-no-valid-station = Invalid station, or empty manifest!
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
general-station-record-console-window-title = Station Records Computer
|
||||||
|
general-station-record-console-select-record-info = Select a record on the left.
|
||||||
|
general-station-record-console-empty-state = No records found!
|
||||||
|
general-station-record-console-no-record-found = No record was found for the selected person.
|
||||||
|
|
||||||
|
general-station-record-console-record-age = Age: {$age}
|
||||||
|
general-station-record-console-record-title = Job: {$job}
|
||||||
|
general-station-record-console-record-species = Species: {$species}
|
||||||
|
general-station-record-console-record-gender = Gender: {$gender}
|
||||||
@@ -65,6 +65,8 @@
|
|||||||
whitelist:
|
whitelist:
|
||||||
components:
|
components:
|
||||||
- IdCard
|
- IdCard
|
||||||
|
- type: CrewManifestViewer
|
||||||
|
unsecure: true
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- DoorBumpOpener
|
- DoorBumpOpener
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
heldPrefix: default
|
heldPrefix: default
|
||||||
- type: Access
|
- type: Access
|
||||||
- type: IdCard
|
- type: IdCard
|
||||||
|
- type: StationRecordKeyStorage
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- DoorBumpOpener
|
- DoorBumpOpener
|
||||||
|
|||||||
@@ -183,6 +183,27 @@
|
|||||||
- type: Computer
|
- type: Computer
|
||||||
board: CriminalRecordsComputerCircuitboard
|
board: CriminalRecordsComputerCircuitboard
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: BaseComputer
|
||||||
|
id: ComputerStationRecords
|
||||||
|
name: station records computer
|
||||||
|
description: This can be used to check station records.
|
||||||
|
components:
|
||||||
|
- type: GeneralStationRecordConsole
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
- key: enum.GeneralStationRecordConsoleKey.Key
|
||||||
|
type: GeneralStationRecordConsoleBoundUserInterface
|
||||||
|
- type: ActivatableUI
|
||||||
|
key: enum.GeneralStationRecordConsoleKey.Key
|
||||||
|
- type: ActivatableUIRequiresPower
|
||||||
|
- type: PointLight
|
||||||
|
radius: 1.5
|
||||||
|
energy: 1.6
|
||||||
|
color: "#1f8c28"
|
||||||
|
- type: Computer
|
||||||
|
board: CriminalRecordsComputerCircuitboard
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: BaseComputer
|
parent: BaseComputer
|
||||||
id: ComputerCrewMonitoring
|
id: ComputerCrewMonitoring
|
||||||
@@ -282,6 +303,7 @@
|
|||||||
interfaces:
|
interfaces:
|
||||||
- key: enum.IdCardConsoleUiKey.Key
|
- key: enum.IdCardConsoleUiKey.Key
|
||||||
type: IdCardConsoleBoundUserInterface
|
type: IdCardConsoleBoundUserInterface
|
||||||
|
- type: CrewManifestViewer
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
visuals:
|
visuals:
|
||||||
- type: ComputerVisualizer
|
- type: ComputerVisualizer
|
||||||
|
|||||||
Reference in New Issue
Block a user