Обновление консоли станционного учета (#2800)
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
parent
fae071e3f5
commit
b38dc9bcce
20 changed files with 856 additions and 11 deletions
|
|
@ -1,3 +1,4 @@
|
|||
using Content.Shared._Sunrise.StationRecords;
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
|
|
@ -22,6 +23,11 @@ public sealed class GeneralStationRecordConsoleBoundUserInterface : BoundUserInt
|
|||
_window.OnFiltersChanged += (type, filterValue) =>
|
||||
SendMessage(new SetStationRecordFilter(type, filterValue));
|
||||
_window.OnDeleted += id => SendMessage(new DeleteStationRecord(id));
|
||||
|
||||
// Sunrise added start
|
||||
_window.OnSaved += (record, id) => SendMessage(new SaveStationRecord(record, id));
|
||||
_window.OnPrinted += id => SendMessage(new PrintStationRecord(id));
|
||||
// Sunrise added end
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@
|
|||
<ItemList Name="RecordListing" />
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Vertical" Margin="5">
|
||||
<BoxContainer Orientation="Vertical" Margin="5" HorizontalExpand="True" VerticalExpand="True">
|
||||
<Label Name="RecordContainerStatus" Visible="False" Text="{Loc 'general-station-record-console-select-record-info'}"/>
|
||||
<Control Name="RecordContainer" Visible="False"/>
|
||||
<BoxContainer Name="RecordContainer" Visible="False" HorizontalExpand="True" VerticalExpand="True"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
using Content.Client._Sunrise.StationRecords;
|
||||
using Content.Client.Lobby;
|
||||
using Content.Client.Roles;
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.StationRecords;
|
||||
|
||||
|
|
@ -17,10 +22,30 @@ public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
|||
|
||||
private StationRecordFilterType _currentFilterType;
|
||||
|
||||
// Sunrise added start
|
||||
public Action<GeneralStationRecord, uint>? OnSaved;
|
||||
public Action<uint>? OnPrinted;
|
||||
|
||||
private readonly IEntityManager _entity;
|
||||
private readonly IPrototypeManager _prototype;
|
||||
private readonly ILocalizationManager _loc;
|
||||
private readonly JobSystem _job;
|
||||
private readonly LobbyUIController _controller;
|
||||
// Sunrise added end
|
||||
|
||||
public GeneralStationRecordConsoleWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
// Sunrise added start
|
||||
_entity = IoCManager.Resolve<IEntityManager>();
|
||||
_prototype = IoCManager.Resolve<IPrototypeManager>();
|
||||
_loc = IoCManager.Resolve<ILocalizationManager>();
|
||||
var interfaceManager = IoCManager.Resolve<IUserInterfaceManager>();
|
||||
_job = _entity.System<JobSystem>();
|
||||
_controller = interfaceManager.GetUIController<LobbyUIController>();
|
||||
// Sunrise added end
|
||||
|
||||
_currentFilterType = StationRecordFilterType.Name;
|
||||
|
||||
foreach (var item in Enum.GetValues<StationRecordFilterType>())
|
||||
|
|
@ -111,7 +136,8 @@ public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
|||
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, state.CanDeleteEntries, state.SelectedKey);
|
||||
// Sunrise edit
|
||||
PopulateRecordContainer(state.Record, state.CanDeleteEntries, state.CanRedactSensitiveData, state.HasAccess, state.SelectedKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -136,11 +162,17 @@ public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
|||
RecordListing.SortItemsByText();
|
||||
}
|
||||
|
||||
private void PopulateRecordContainer(GeneralStationRecord record, bool enableDelete, uint? id)
|
||||
// Sunrise edit
|
||||
private void PopulateRecordContainer(GeneralStationRecord record, bool enableDelete, bool canRedactSensitiveData, bool hasAccess, uint? id)
|
||||
{
|
||||
RecordContainer.RemoveAllChildren();
|
||||
var newRecord = new GeneralRecord(record, enableDelete, id);
|
||||
// Sunrise edit start
|
||||
var newRecord =
|
||||
new SunriseGeneralRecord(record, enableDelete, canRedactSensitiveData, hasAccess, id, in _entity, in _prototype, in _loc, in _job, in _controller);
|
||||
// Sunrise edit end
|
||||
newRecord.OnDeletePressed = OnDeleted;
|
||||
newRecord.OnPrintPressed = OnPrinted;
|
||||
newRecord.OnSaveButtonPressed = OnSaved;
|
||||
|
||||
RecordContainer.AddChild(newRecord);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True"
|
||||
MinSize="500 600">
|
||||
<!-- Заголовок с именем, работой и отделом-->
|
||||
<controls:StripeBack VerticalAlignment="Top" HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Vertical" VerticalAlignment="Top">
|
||||
<Label Name="NameHeading" Access="Public" StyleClasses="LabelHeading" HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
|
||||
<BoxContainer Orientation="Horizontal" Align="Center">
|
||||
<Label Name="SubText" VerticalAlignment="Center" HorizontalAlignment="Center" StyleClasses="LabelSubText" Access="Public"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:StripeBack>
|
||||
<!-- Спрайт персонажа посередине окна-->
|
||||
<SpriteView OverrideDirection="South" Scale="8 8" Name="SpriteView" Access="Public" MinSize="256 256" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5 25 5 25"/>
|
||||
<controls:StripeBack VerticalAlignment="Bottom" HorizontalExpand="True" VerticalExpand="True">
|
||||
<!-- Нижняя часть-->
|
||||
<BoxContainer Orientation="Horizontal" VerticalAlignment="Center" Margin="5 5 5 5">
|
||||
<!-- Белая полоска слева -->
|
||||
<PanelContainer
|
||||
VerticalExpand="True"
|
||||
MinWidth="2"
|
||||
MaxWidth="2"
|
||||
Margin="9 5 9 0">
|
||||
<PanelContainer.PanelOverride>
|
||||
<graphics:StyleBoxFlat BackgroundColor="#CFCFCF"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
</PanelContainer>
|
||||
<!-- Поля для изменения данных -->
|
||||
<GridContainer Name="ControlGrid" Columns="2" HorizontalAlignment="Stretch" HorizontalExpand="True">
|
||||
<Label Name="NameLabel" Text="{Loc 'station-records-ui-name'}"/> <LineEdit Name="Name" HorizontalExpand="True"/>
|
||||
<Label Name="AgeLabel" Text="{Loc 'station-records-ui-age'}"/> <LineEdit Name="Age"/>
|
||||
<Label Name="GenderLabel" Text="{Loc 'station-records-ui-gender'}"/> <OptionButton Name="Gender"/>
|
||||
<Label Name="SpeciesLabel" Text="{Loc 'station-records-ui-species'}"/> <OptionButton Name="Species"/>
|
||||
<Label Name="JobLabel" Text="{Loc 'station-records-ui-job'}"/> <OptionButton Name="Job"/>
|
||||
<Label Name="FingerprintLabel" Text="{Loc 'station-records-ui-fingerprint'}"/> <LineEdit Name="Fingerprint" Editable="False"/>
|
||||
<Label Name="DnaLabel" Text="{Loc 'station-records-ui-dna'}"/> <LineEdit Name="Dna" Editable="False"/>
|
||||
</GridContainer>
|
||||
</BoxContainer>
|
||||
</controls:StripeBack>
|
||||
|
||||
<!-- Кнопки сохранить, распечатать и удалить(не видна без емага) -->
|
||||
<GridContainer Columns="3" HorizontalAlignment="Stretch" HorizontalExpand="True">
|
||||
<Button Visible="False" Name="SaveButton" Text="{Loc 'station-records-ui-save'}" Disabled="True" StyleClasses="ButtonColorGreen"/>
|
||||
<Button Visible="False" Name="PrintButton" Text="{Loc 'station-records-ui-print'}"/>
|
||||
<Button Visible="False" Name="DeleteButton" Text="{Loc 'general-station-record-console-delete'}" StyleClasses="ButtonColorRed"/>
|
||||
</GridContainer>
|
||||
|
||||
<!-- Характеристика -->
|
||||
<controls:StripeBack VerticalAlignment="Bottom" HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Horizontal" VerticalAlignment="Center" Margin="5 5 5 5">
|
||||
<!-- Опять белая полоска слева -->
|
||||
<PanelContainer
|
||||
VerticalExpand="True"
|
||||
MinWidth="2"
|
||||
MaxWidth="2"
|
||||
Margin="9 5 9 0">
|
||||
<PanelContainer.PanelOverride>
|
||||
<graphics:StyleBoxFlat BackgroundColor="#CFCFCF"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
</PanelContainer>
|
||||
<GridContainer Columns="1" HorizontalAlignment="Stretch" HorizontalExpand="True">
|
||||
<Label Name="PersonalityLabel" Text="{Loc 'station-records-ui-personality'}"/>
|
||||
<TextEdit Name="Personality" HorizontalExpand="True" VerticalExpand="True" MinSize="300 100"/>
|
||||
</GridContainer>
|
||||
</BoxContainer>
|
||||
</controls:StripeBack>
|
||||
</BoxContainer>
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
using System.Linq;
|
||||
using Content.Client.Lobby;
|
||||
using Content.Client.Roles;
|
||||
using Content.Shared._Sunrise.Helpers;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client._Sunrise.StationRecords;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class SunriseGeneralRecord : BoxContainer
|
||||
{
|
||||
private readonly IEntityManager _entity;
|
||||
private readonly IPrototypeManager _prototype;
|
||||
private readonly ILocalizationManager _loc;
|
||||
private readonly JobSystem _job;
|
||||
private readonly LobbyUIController _controller;
|
||||
|
||||
public Action<GeneralStationRecord, uint>? OnSaveButtonPressed;
|
||||
public Action<uint>? OnPrintPressed;
|
||||
public Action<uint>? OnDeletePressed;
|
||||
|
||||
private EntityUid _previewDummy;
|
||||
private readonly HumanoidCharacterProfile? _profile;
|
||||
|
||||
private readonly List<SpeciesPrototype> _allSpecies;
|
||||
private readonly List<JobPrototype> _allJobs;
|
||||
private readonly Gender[] _allGender;
|
||||
|
||||
private readonly bool _hasAccess;
|
||||
|
||||
// Копия этого хранится в серверной системе
|
||||
private const int MaxAgeLength = 6;
|
||||
|
||||
public SunriseGeneralRecord(GeneralStationRecord record,
|
||||
bool canDelete,
|
||||
bool canRedactSensitiveData,
|
||||
bool hasAccess,
|
||||
uint? id,
|
||||
in IEntityManager entity,
|
||||
in IPrototypeManager prototype,
|
||||
in ILocalizationManager loc,
|
||||
in JobSystem job,
|
||||
in LobbyUIController controller)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
_entity = entity;
|
||||
_prototype = prototype;
|
||||
_loc = loc;
|
||||
_job = job;
|
||||
_controller = controller;
|
||||
|
||||
_profile = record.HumanoidProfile;
|
||||
_allSpecies = _prototype.EnumeratePrototypes<SpeciesPrototype>().ToList();
|
||||
_allJobs = _prototype.EnumeratePrototypes<JobPrototype>().ToList();
|
||||
_allGender = Enum.GetValues<Gender>();
|
||||
|
||||
// Сортировка рас и работ по имени
|
||||
_allSpecies.Sort((a, b)
|
||||
=> string.Compare(_loc.GetString(a.Name), _loc.GetString(b.Name), StringComparison.Ordinal));
|
||||
|
||||
_allJobs.Sort((a, b)
|
||||
=> string.Compare(a.LocalizedName, b.LocalizedName, StringComparison.Ordinal));
|
||||
|
||||
Fingerprint.Editable = canRedactSensitiveData && hasAccess;
|
||||
Dna.Editable = canRedactSensitiveData && hasAccess;
|
||||
|
||||
if (canDelete && id != null )
|
||||
{
|
||||
DeleteButton.Visible = true;
|
||||
DeleteButton.OnPressed += _ => OnDeletePressed?.Invoke(id.Value);
|
||||
}
|
||||
|
||||
if (id != null )
|
||||
{
|
||||
SaveButton.Visible = true;
|
||||
SaveButton.OnPressed += _ =>
|
||||
{
|
||||
var updatedRecord = BuildUpdatedRecord(record);
|
||||
OnSaveButtonPressed?.Invoke(updatedRecord, id.Value);
|
||||
};
|
||||
}
|
||||
|
||||
if (id != null )
|
||||
{
|
||||
PrintButton.Visible = true;
|
||||
PrintButton.OnPressed += _ => OnPrintPressed?.Invoke(id.Value);
|
||||
}
|
||||
|
||||
_hasAccess = hasAccess;
|
||||
|
||||
UpdateEditableInfo(record);
|
||||
UpdateHeading(record);
|
||||
ReloadPreview(record.JobPrototype);
|
||||
|
||||
MakeDropDownSelectable();
|
||||
CheckAccess();
|
||||
CheckChanges();
|
||||
}
|
||||
|
||||
protected override void ExitedTree()
|
||||
{
|
||||
base.ExitedTree();
|
||||
|
||||
_entity.DeleteEntity(_previewDummy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обновляет информацию, которая может быть изменена игроком.
|
||||
/// </summary>
|
||||
private void UpdateEditableInfo(GeneralStationRecord record)
|
||||
{
|
||||
Name.Text = record.Name;
|
||||
Age.Text = record.Age.ToString();
|
||||
|
||||
for (var i = 0; i < _allGender.Length; i++)
|
||||
{
|
||||
var item = _allGender[i];
|
||||
var name = _loc.GetString("station-records-gender", ("gender", item.ToString()));
|
||||
|
||||
Gender.AddItem(name, i);
|
||||
|
||||
if (item == record.Gender)
|
||||
Gender.SelectId(i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < _allSpecies.Count; i++)
|
||||
{
|
||||
var item = _allSpecies[i];
|
||||
|
||||
if (item.StationRecordsHidden && item.ID != record.Species)
|
||||
continue;
|
||||
|
||||
var name = _loc.GetString(item.Name);
|
||||
Species.AddItem(name, i);
|
||||
|
||||
if (item.ID == record.Species)
|
||||
Species.SelectId(i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < _allJobs.Count; i++)
|
||||
{
|
||||
var item = _allJobs[i];
|
||||
|
||||
if (item.OverrideConsoleVisibility == false && item.ID != record.JobPrototype)
|
||||
continue;
|
||||
|
||||
var name = item.LocalizedName;
|
||||
Job.AddItem(name, i);
|
||||
|
||||
if (item.ID == record.JobPrototype)
|
||||
Job.SelectId(i);
|
||||
}
|
||||
|
||||
Fingerprint.Text = record.Fingerprint ?? _loc.GetString("generic-not-available-shorthand");
|
||||
Dna.Text = record.DNA ?? _loc.GetString("generic-not-available-shorthand");
|
||||
|
||||
Personality.Placeholder = new Rope.Leaf(_loc.GetString("station-records-ui-personality-placeholder"));
|
||||
|
||||
if (!string.IsNullOrEmpty(record.Personality))
|
||||
Personality.TextRope = new Rope.Leaf(record.Personality);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обновляет заголовок сверху персонажа.
|
||||
/// </summary>
|
||||
private void UpdateHeading(GeneralStationRecord record)
|
||||
{
|
||||
SubText.Visible = false;
|
||||
NameHeading.Text = record.Name;
|
||||
|
||||
if (!_prototype.TryIndex<JobPrototype>(record.JobPrototype, out var job))
|
||||
return;
|
||||
|
||||
SubText.Text = $"{job.LocalizedName}";
|
||||
SubText.Visible = true;
|
||||
|
||||
if (!_job.TryGetDepartment(job.ID, out var department))
|
||||
return;
|
||||
|
||||
SubText.Text = $"{_loc.GetString(department.Name)}, {job.LocalizedName}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обновляет превью игрока.
|
||||
/// </summary>
|
||||
private void ReloadPreview(ProtoId<JobPrototype> jobProtoId)
|
||||
{
|
||||
if (!_prototype.TryIndex(jobProtoId, out var job))
|
||||
return;
|
||||
|
||||
_entity.DeleteEntity(_previewDummy);
|
||||
_previewDummy = EntityUid.Invalid;
|
||||
|
||||
if (job.JobEntity != null)
|
||||
_previewDummy = _entity.Spawn(job.JobEntity, MapCoordinates.Nullspace);
|
||||
|
||||
if (_profile != null && _prototype.HasIndex(_profile.Species))
|
||||
_previewDummy = _controller.LoadProfileEntity(_profile, job, true);
|
||||
|
||||
SpriteView.SetEntity(_previewDummy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создает новую структуру, помещающую в себя из полей в интерфейсе для отправки на сервере для сохранения.
|
||||
/// Перед этим проводит валидацию строк и проверяет, что данные не обосраны.
|
||||
/// </summary>
|
||||
private GeneralStationRecord BuildUpdatedRecord(GeneralStationRecord original)
|
||||
{
|
||||
var textAge = Age.Text.SanitizeInput(MaxAgeLength);
|
||||
|
||||
var updated = original with
|
||||
{
|
||||
Name = Name.Text,
|
||||
Age = int.TryParse(textAge, out var ageVal) ? ageVal : original.Age,
|
||||
Gender = _allGender[Gender.SelectedId],
|
||||
Species = _allSpecies[Species.SelectedId].ID,
|
||||
JobPrototype = _allJobs[Job.SelectedId].ID,
|
||||
Fingerprint = Fingerprint.Text,
|
||||
DNA = Dna.Text,
|
||||
Personality = Rope.Collapse(Personality.TextRope),
|
||||
};
|
||||
|
||||
return GeneralStationRecord.SanitizeRecord(updated, in _prototype);
|
||||
}
|
||||
|
||||
private void MakeDropDownSelectable()
|
||||
{
|
||||
// Почему это не встроено в сами кнопки
|
||||
foreach (var child in ControlGrid.Children)
|
||||
{
|
||||
if (child is not OptionButton optionButton)
|
||||
continue;
|
||||
|
||||
optionButton.OnItemSelected += Select;
|
||||
}
|
||||
}
|
||||
|
||||
private void Select(OptionButton.ItemSelectedEventArgs args)
|
||||
{
|
||||
args.Button.SelectId(args.Id);
|
||||
MakeSaveAvailable();
|
||||
}
|
||||
|
||||
private void CheckAccess()
|
||||
{
|
||||
Name.Editable = _hasAccess;
|
||||
Age.Editable = _hasAccess;
|
||||
Personality.Editable = _hasAccess;
|
||||
Gender.Disabled = !_hasAccess;
|
||||
Species.Disabled = !_hasAccess;
|
||||
Job.Disabled = !_hasAccess;
|
||||
}
|
||||
|
||||
private void CheckChanges()
|
||||
{
|
||||
foreach (var child in ControlGrid.Children)
|
||||
{
|
||||
if (child is not LineEdit lineEdit)
|
||||
continue;
|
||||
|
||||
lineEdit.OnTextChanged += _ => MakeSaveAvailable();
|
||||
}
|
||||
|
||||
Personality.OnTextChanged += _ => MakeSaveAvailable();
|
||||
}
|
||||
|
||||
private void MakeSaveAvailable()
|
||||
{
|
||||
SaveButton.Disabled = !_hasAccess;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
using Content.Server.StationRecords.Systems;
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.StationRecords.Components;
|
||||
|
||||
|
|
@ -24,4 +27,38 @@ public sealed partial class GeneralStationRecordConsoleComponent : Component
|
|||
/// </summary>
|
||||
[DataField]
|
||||
public bool CanDeleteEntries;
|
||||
|
||||
// Sunrise added start - возможность редактировать отпечатки через емаг
|
||||
[DataField]
|
||||
public bool CanRedactSensitiveData;
|
||||
|
||||
[DataField]
|
||||
public bool HasAccess;
|
||||
|
||||
[DataField]
|
||||
public bool Silent;
|
||||
|
||||
[DataField]
|
||||
public bool SkipAccessCheck;
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier SuccessfulSound = new SoundPathSpecifier("/Audio/Effects/Cargo/ping.ogg");
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier FailedSound = new SoundPathSpecifier("/Audio/Effects/Cargo/buzz_sigh.ogg");
|
||||
|
||||
public TimeSpan NextPrintTime = TimeSpan.Zero;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan PrintCooldown = TimeSpan.FromSeconds(5);
|
||||
|
||||
[DataField]
|
||||
public EntProtoId Paper = "Paper";
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier SoundPrint = new SoundPathSpecifier("/Audio/Machines/short_print_and_rip.ogg");
|
||||
|
||||
[DataField]
|
||||
public List<ProtoId<RadioChannelPrototype>> AnnouncementChannels = ["Command", "Security"];
|
||||
// Sunrise added end
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using Robust.Server.GameObjects;
|
|||
|
||||
namespace Content.Server.StationRecords.Systems;
|
||||
|
||||
public sealed class GeneralStationRecordConsoleSystem : EntitySystem
|
||||
public sealed partial class GeneralStationRecordConsoleSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
|
|
@ -20,11 +20,14 @@ public sealed class GeneralStationRecordConsoleSystem : EntitySystem
|
|||
|
||||
Subs.BuiEvents<GeneralStationRecordConsoleComponent>(GeneralStationRecordConsoleKey.Key, subs =>
|
||||
{
|
||||
subs.Event<BoundUIOpenedEvent>(UpdateUserInterface);
|
||||
subs.Event<BoundUIOpenedEvent>(OnOpened);
|
||||
subs.Event<SelectStationRecord>(OnKeySelected);
|
||||
subs.Event<SetStationRecordFilter>(OnFiltersChanged);
|
||||
subs.Event<DeleteStationRecord>(OnRecordDelete);
|
||||
});
|
||||
|
||||
// Sunrise added
|
||||
InitializeSunrise();
|
||||
}
|
||||
|
||||
private void OnRecordDelete(Entity<GeneralStationRecordConsoleComponent> ent, ref DeleteStationRecord args)
|
||||
|
|
@ -34,6 +37,23 @@ public sealed class GeneralStationRecordConsoleSystem : EntitySystem
|
|||
|
||||
var owning = _station.GetOwningStation(ent.Owner);
|
||||
|
||||
// Sunrise added start
|
||||
if (owning == null)
|
||||
return;
|
||||
|
||||
// Дополнительная серверная проверка на случай педиков с читами
|
||||
if (!HasAccess(ent, args.Actor))
|
||||
return;
|
||||
|
||||
if (!_stationRecords.TryGetRecord<GeneralStationRecord>(new StationRecordKey(args.Id, owning.Value), out var record))
|
||||
return;
|
||||
|
||||
var message = Loc.GetString("station-record-deleted", ("name", record.Name));
|
||||
var popup = Loc.GetString("station-record-deleted-successfully");
|
||||
|
||||
DoFeedback(ent, message, popup);
|
||||
// Sunrise added end
|
||||
|
||||
if (owning != null)
|
||||
_stationRecords.RemoveRecord(new StationRecordKey(args.Id, owning.Value));
|
||||
UpdateUserInterface(ent); // Apparently an event does not get raised for this.
|
||||
|
|
@ -93,7 +113,8 @@ public sealed class GeneralStationRecordConsoleSystem : EntitySystem
|
|||
var key = new StationRecordKey(id, owningStation.Value);
|
||||
_stationRecords.TryGetRecord<GeneralStationRecord>(key, out var record, stationRecords);
|
||||
|
||||
GeneralStationRecordConsoleState newState = new(id, record, listing, console.Filter, ent.Comp.CanDeleteEntries);
|
||||
// Sunrise edit
|
||||
GeneralStationRecordConsoleState newState = new(id, record, listing, console.Filter, ent.Comp.CanDeleteEntries, ent.Comp.CanRedactSensitiveData, ent.Comp.HasAccess);
|
||||
_ui.SetUiState(uid, GeneralStationRecordConsoleKey.Key, newState);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,6 +179,7 @@ public sealed class StationRecordsSystem : SharedStationRecordsSystem
|
|||
Fingerprint = mobFingerprint,
|
||||
DNA = dna,
|
||||
Silicon = silicon, // Sunrise-Edit
|
||||
HumanoidProfile = profile, // Sunrise edit
|
||||
};
|
||||
|
||||
var key = AddRecordEntry(station, record);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,234 @@
|
|||
using Content.Server.Hands.Systems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Radio.EntitySystems;
|
||||
using Content.Server.Roles.Jobs;
|
||||
using Content.Server.StationRecords.Components;
|
||||
using Content.Shared._Sunrise.StationRecords;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Emag.Systems;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Paper;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Server.Audio;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.StationRecords.Systems;
|
||||
|
||||
public sealed partial class GeneralStationRecordConsoleSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly AudioSystem _audio = default!;
|
||||
[Dependency] private readonly RadioSystem _radio = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly AccessReaderSystem _access = default!;
|
||||
[Dependency] private readonly HandsSystem _hands = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||
[Dependency] private readonly JobSystem _job = default!;
|
||||
[Dependency] private readonly PaperSystem _paper = default!;
|
||||
|
||||
private void InitializeSunrise()
|
||||
{
|
||||
SubscribeLocalEvent<GeneralStationRecordConsoleComponent, GotEmaggedEvent>(OnEmagged);
|
||||
|
||||
Subs.BuiEvents<GeneralStationRecordConsoleComponent>(GeneralStationRecordConsoleKey.Key, subs =>
|
||||
{
|
||||
subs.Event<SaveStationRecord>(OnSave);
|
||||
subs.Event<PrintStationRecord>(Print);
|
||||
});
|
||||
}
|
||||
|
||||
private void OnSave(Entity<GeneralStationRecordConsoleComponent> ent, ref SaveStationRecord args)
|
||||
{
|
||||
var owning = _station.GetOwningStation(ent.Owner);
|
||||
|
||||
if (owning == null)
|
||||
{
|
||||
_audio.PlayPvs(ent.Comp.FailedSound, ent);
|
||||
return;
|
||||
}
|
||||
|
||||
// Дополнительная серверная проверка на случай педиков с читами
|
||||
if (!HasAccess(ent, args.Actor))
|
||||
{
|
||||
_audio.PlayPvs(ent.Comp.FailedSound, ent);
|
||||
return;
|
||||
}
|
||||
|
||||
// Удаляем старую запись
|
||||
if (!_stationRecords.RemoveRecord(new StationRecordKey(args.Id, owning.Value)))
|
||||
{
|
||||
_audio.PlayPvs(ent.Comp.FailedSound, ent);
|
||||
return;
|
||||
}
|
||||
|
||||
// Добавляем новую
|
||||
var record = GeneralStationRecord.SanitizeRecord(args.Record, in _prototype);
|
||||
var id = _stationRecords.AddRecordEntry(owning.Value, record);
|
||||
ent.Comp.ActiveKey = id.Id;
|
||||
|
||||
var message = Loc.GetString("station-record-updated", ("name", args.Record.Name));
|
||||
var popup = Loc.GetString("station-record-updated-successfully");
|
||||
|
||||
DoFeedback(ent, message, popup);
|
||||
|
||||
UpdateUserInterface(ent);
|
||||
}
|
||||
|
||||
private void OnEmagged(Entity<GeneralStationRecordConsoleComponent> ent, ref GotEmaggedEvent args)
|
||||
{
|
||||
if (ent.Comp.CanRedactSensitiveData
|
||||
&& ent.Comp.CanDeleteEntries
|
||||
&& ent.Comp.Silent
|
||||
&& ent.Comp.SkipAccessCheck)
|
||||
return;
|
||||
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
ent.Comp.CanDeleteEntries = true;
|
||||
ent.Comp.CanRedactSensitiveData = true;
|
||||
ent.Comp.Silent = true;
|
||||
ent.Comp.SkipAccessCheck = true;
|
||||
|
||||
UpdateUserInterface(ent);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void Print(Entity<GeneralStationRecordConsoleComponent> ent, ref PrintStationRecord args)
|
||||
{
|
||||
var user = args.Actor;
|
||||
|
||||
if (_timing.CurTime < ent.Comp.NextPrintTime)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("forensic-scanner-printer-not-ready"), ent, user);
|
||||
_audio.PlayPvs(ent.Comp.FailedSound, ent);
|
||||
return;
|
||||
}
|
||||
|
||||
var owning = _station.GetOwningStation(ent.Owner);
|
||||
|
||||
if (owning == null)
|
||||
{
|
||||
_audio.PlayPvs(ent.Comp.FailedSound, ent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_stationRecords.TryGetRecord<GeneralStationRecord>(new StationRecordKey(args.Id, owning.Value), out var record))
|
||||
{
|
||||
_audio.PlayPvs(ent.Comp.FailedSound, ent);
|
||||
return;
|
||||
}
|
||||
|
||||
// Spawn a piece of paper.
|
||||
var printed = Spawn(ent.Comp.Paper, Transform(ent).Coordinates);
|
||||
_hands.PickupOrDrop(args.Actor, printed, checkActionBlocker: false);
|
||||
|
||||
if (!TryComp<PaperComponent>(printed, out var paperComp))
|
||||
{
|
||||
_audio.PlayPvs(ent.Comp.FailedSound, ent);
|
||||
return;
|
||||
}
|
||||
|
||||
var documentName = Loc.GetString("printed-station-records-document-name", ("name", record.Name));
|
||||
_metaData.SetEntityName(printed, documentName);
|
||||
|
||||
var text = Loc.GetString(
|
||||
"printed-station-records-content",
|
||||
("name", record.Name),
|
||||
("job", GetJobName(record.JobPrototype)),
|
||||
("department", GetDepartmentName(record.JobPrototype)),
|
||||
("age", record.Age),
|
||||
("gender", GetGenderName(record.Gender)),
|
||||
("species", GetSpeciesName(record.Species)),
|
||||
("dna", record.DNA ?? Loc.GetString("printed-station-records-unrecognized")),
|
||||
("fingerprint", record.Fingerprint ?? Loc.GetString("printed-station-records-unrecognized")),
|
||||
("personality", GetPersonality(record.Personality))
|
||||
);
|
||||
|
||||
_paper.SetContent((printed, paperComp), text);
|
||||
_audio.PlayPvs(ent.Comp.SoundPrint, ent,
|
||||
AudioParams.Default
|
||||
.WithVariation(0.25f)
|
||||
.WithVolume(4f)
|
||||
.WithRolloffFactor(2.8f)
|
||||
.WithMaxDistance(4.5f));
|
||||
|
||||
ent.Comp.NextPrintTime = _timing.CurTime + ent.Comp.PrintCooldown;
|
||||
}
|
||||
|
||||
private void DoFeedback(Entity<GeneralStationRecordConsoleComponent> ent, string message, string popup)
|
||||
{
|
||||
_popup.PopupEntity(popup, ent);
|
||||
|
||||
if (ent.Comp.Silent)
|
||||
return;
|
||||
|
||||
foreach (var channel in ent.Comp.AnnouncementChannels)
|
||||
{
|
||||
_radio.SendRadioMessage(ent, message, channel, ent);
|
||||
}
|
||||
|
||||
_audio.PlayPvs(ent.Comp.SuccessfulSound, ent);
|
||||
}
|
||||
|
||||
private void OnOpened(Entity<GeneralStationRecordConsoleComponent> ent, ref BoundUIOpenedEvent msg)
|
||||
{
|
||||
ent.Comp.HasAccess = HasAccess(ent, msg.Actor);
|
||||
UpdateUserInterface(ent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет наличие у персонажа доступа к консоли.
|
||||
/// </summary>
|
||||
private bool HasAccess(Entity<GeneralStationRecordConsoleComponent> ent, EntityUid actor)
|
||||
{
|
||||
var allowed = _access.IsAllowed(actor, ent);
|
||||
return allowed || ent.Comp.SkipAccessCheck;
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
private string GetJobName(ProtoId<JobPrototype> job)
|
||||
{
|
||||
if (!_prototype.TryIndex(job, out var jobPrototype))
|
||||
return Loc.GetString("printed-station-records-unrecognized");
|
||||
|
||||
return jobPrototype.LocalizedName;
|
||||
}
|
||||
|
||||
private string GetDepartmentName(ProtoId<JobPrototype> job)
|
||||
{
|
||||
if (!_job.TryGetDepartment(job, out var department))
|
||||
return Loc.GetString("printed-station-records-unrecognized");
|
||||
|
||||
return Loc.GetString(department.Name);
|
||||
}
|
||||
|
||||
private string GetGenderName(Gender gender)
|
||||
{
|
||||
return Loc.GetString("station-records-gender", ("gender", gender.ToString()));
|
||||
}
|
||||
|
||||
private string GetSpeciesName(ProtoId<SpeciesPrototype> species)
|
||||
{
|
||||
if (!_prototype.TryIndex(species, out var speciesPrototype))
|
||||
return Loc.GetString("printed-station-records-unrecognized");
|
||||
|
||||
return Loc.GetString(speciesPrototype.Name);
|
||||
}
|
||||
|
||||
private string GetPersonality(string personality)
|
||||
{
|
||||
if (string.IsNullOrEmpty(personality))
|
||||
return Loc.GetString("printed-station-records-unrecognized");
|
||||
|
||||
return personality;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -199,6 +199,9 @@ public sealed partial class SpeciesPrototype : IPrototype
|
|||
/// </summary>
|
||||
[DataField]
|
||||
public int StandardDensity = 120;
|
||||
|
||||
[DataField]
|
||||
public bool StationRecordsHidden;
|
||||
//Sunrise end
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -38,21 +38,28 @@ public sealed class GeneralStationRecordConsoleState : BoundUserInterfaceState
|
|||
public readonly Dictionary<uint, string>? RecordListing;
|
||||
public readonly StationRecordsFilter? Filter;
|
||||
public readonly bool CanDeleteEntries;
|
||||
public readonly bool CanRedactSensitiveData; // Sunrise added
|
||||
public readonly bool HasAccess; // Sunrise added
|
||||
|
||||
public GeneralStationRecordConsoleState(uint? key,
|
||||
GeneralStationRecord? record,
|
||||
Dictionary<uint, string>? recordListing,
|
||||
StationRecordsFilter? newFilter,
|
||||
bool canDeleteEntries)
|
||||
bool canDeleteEntries,
|
||||
bool canRedactSensitiveData, // Sunrise added
|
||||
bool hasAccess) // Sunrise added
|
||||
{
|
||||
SelectedKey = key;
|
||||
Record = record;
|
||||
RecordListing = recordListing;
|
||||
Filter = newFilter;
|
||||
CanDeleteEntries = canDeleteEntries;
|
||||
CanRedactSensitiveData = canRedactSensitiveData; // Sunrise added
|
||||
HasAccess = hasAccess; // Sunrise added
|
||||
}
|
||||
|
||||
public GeneralStationRecordConsoleState() : this(null, null, null, null, false)
|
||||
// Sunrise edit
|
||||
public GeneralStationRecordConsoleState() : this(null, null, null, null, false, false, false)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
using Content.Shared._Sunrise.Helpers;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.StationRecords;
|
||||
|
|
@ -69,6 +74,43 @@ public sealed record GeneralStationRecord
|
|||
[DataField]
|
||||
public string? DNA;
|
||||
|
||||
// Sunrise added start
|
||||
[DataField]
|
||||
public bool Silicon; // Sunrise-Edit
|
||||
public bool Silicon;
|
||||
|
||||
[DataField]
|
||||
public HumanoidCharacterProfile? HumanoidProfile;
|
||||
|
||||
[DataField]
|
||||
public string Personality = string.Empty;
|
||||
|
||||
[NonSerialized] private const int MaxNameLength = 64;
|
||||
[NonSerialized] private const int MaxAge = 10000;
|
||||
[NonSerialized] private const int MaxFingerprintLength = 32;
|
||||
[NonSerialized] private const int MaxDnaLength = 16;
|
||||
[NonSerialized] private const int MaxPersonalityLength = 1024;
|
||||
|
||||
[NonSerialized] private static readonly ProtoId<JobPrototype> FallbackJobPrototype = "Passenger";
|
||||
[NonSerialized] private static readonly ProtoId<SpeciesPrototype> FallbackSpeciesPrototype = "Human";
|
||||
|
||||
/// <summary>
|
||||
/// Санитизирует данные, требуется для механики изменения и сохранения данных в консоли станционного учета.
|
||||
/// </summary>
|
||||
public static GeneralStationRecord SanitizeRecord(GeneralStationRecord original, in IPrototypeManager prototype)
|
||||
{
|
||||
var updated = original with
|
||||
{
|
||||
Name = original.Name.SanitizeInput(MaxNameLength),
|
||||
Age = original.Age <= MaxAge ? original.Age : MaxAge,
|
||||
Species = prototype.TryIndex<SpeciesPrototype>(original.Species, out var species) ? species.ID : FallbackSpeciesPrototype,
|
||||
JobPrototype = prototype.TryIndex<JobPrototype>(original.JobPrototype, out var job) ? job.ID : FallbackJobPrototype,
|
||||
Fingerprint = original.Fingerprint.SanitizeInput(MaxFingerprintLength),
|
||||
DNA = original.DNA.SanitizeInput(MaxDnaLength),
|
||||
Personality = original.Personality.SanitizeInput(MaxPersonalityLength),
|
||||
};
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
// Sunrise added end
|
||||
}
|
||||
|
|
|
|||
39
Content.Shared/_Sunrise/Helpers/StringExtensions.cs
Normal file
39
Content.Shared/_Sunrise/Helpers/StringExtensions.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Content.Shared._Sunrise.Helpers;
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
private static readonly Regex AllowedCharsRegex = new Regex(
|
||||
@"[^a-zA-Zа-яА-ЯёЁ0-9\s.,!?;:\-_\(\)\[\]{}""'/\\@#%\^&\*\+=<>]",
|
||||
RegexOptions.Compiled | RegexOptions.CultureInvariant
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Санитизация пользовательского ввода:
|
||||
/// - Убирает лишние пробелы по краям
|
||||
/// - Обрезает до maxLength (если задан)
|
||||
/// - Убирает недопустимые символы
|
||||
/// - Нормализует Unicode
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Рекомендуется для UI, содержащих LineEdit или подобные возможности передать текст на сервер.
|
||||
/// </remarks>
|
||||
public static string SanitizeInput(this string? input, int? maxLength = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return string.Empty;
|
||||
|
||||
input = input.Trim();
|
||||
|
||||
if (maxLength.HasValue && input.Length > maxLength.Value)
|
||||
input = input.Substring(0, maxLength.Value);
|
||||
|
||||
input = input.Normalize(NormalizationForm.FormC);
|
||||
|
||||
input = AllowedCharsRegex.Replace(input, string.Empty);
|
||||
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using Content.Shared.StationRecords;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._Sunrise.StationRecords;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class SaveStationRecord(GeneralStationRecord record, uint id) : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly uint Id = id;
|
||||
public readonly GeneralStationRecord Record = record;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PrintStationRecord(uint id) : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly uint Id = id;
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
printed-station-records-content =
|
||||
|
||||
|
||||
[head=1]NanoTransen[/head]
|
||||
[bold]Распечатка из базы данных экипажа станции[/bold]
|
||||
▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
|
||||
|
||||
[head=2]Основная информация[/head]
|
||||
|
||||
[bullet] [color=#594D4A][bold]Имя:[/bold][/color] [italic]{$name}[/italic]
|
||||
[bullet] [color=#594D4A][bold]Должность:[/bold][/color] [italic]{$job}[/italic]
|
||||
[bullet] [color=#594D4A][bold]Отдел:[/bold][/color] [italic]{$department}[/italic]
|
||||
[bullet] [color=#594D4A][bold]Возраст:[/bold][/color] [italic]{$age}[/italic]
|
||||
[bullet] [color=#594D4A][bold]Пол:[/bold][/color] [italic]{$gender}[/italic]
|
||||
[bullet] [color=#594D4A][bold]Раса:[/bold][/color] [italic]{$species}[/italic]
|
||||
|
||||
[head=2]Биометрические данные[/head]
|
||||
|
||||
[bullet] [color=#5BA4CF][bold]ДНК:[/bold][/color] [mono]{$dna}[/mono]
|
||||
[bullet] [color=#5BA4CF][bold]Отпечаток пальцев:[/bold][/color] [mono]{$fingerprint}[/mono]
|
||||
|
||||
[head=2]Характеристика[/head]
|
||||
|
||||
[italic]{$personality}[/italic]
|
||||
|
||||
printed-station-records-document-name = Распечатка на {$name}
|
||||
printed-station-records-unrecognized = Неустановлено
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
station-record-updated = Запись в базе данных для «{$name}» была обновлена!
|
||||
station-record-deleted = [bold]Запись в базе данных для «{$name}» была УДАЛЕНА![/bold]
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
station-records-gender =
|
||||
{ $gender ->
|
||||
[Male] Мужской
|
||||
[Female] Женский
|
||||
[Epicene] Двуполый
|
||||
*[Neuter] Бесполый
|
||||
}
|
||||
|
||||
station-records-ui-name = Имя:
|
||||
station-records-ui-age = Возраст:
|
||||
station-records-ui-job = Должность:
|
||||
station-records-ui-species = Раса:
|
||||
station-records-ui-gender = Пол:
|
||||
station-records-ui-fingerprint = Отпечатки:
|
||||
station-records-ui-dna = ДНК:
|
||||
station-records-ui-personality = Характеристика:
|
||||
station-records-ui-personality-placeholder = Здесь будет текст...
|
||||
|
||||
station-records-ui-save = Сохранить
|
||||
station-records-ui-print = Распечатать
|
||||
|
|
@ -408,6 +408,10 @@
|
|||
name: station records computer
|
||||
description: This can be used to check station records.
|
||||
components:
|
||||
# Sunrise added start
|
||||
- type: AccessReader
|
||||
access: [["Security"], ["HeadOfPersonnel"]]
|
||||
# Sunrise added end
|
||||
- type: GeneralStationRecordConsole
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
dollPrototype: MobSkeletonPersonDummy
|
||||
skinColoration: TintedHues
|
||||
buttScanTexture: /Textures/_Sunrise/ButtsScans/human.png # Sunrise-Edit
|
||||
stationRecordsHidden: true
|
||||
|
||||
- type: bodyType
|
||||
id: TerminatorNormal
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
defaultSkinTone: "#BFC2C7"
|
||||
defaultHeight: 1.05
|
||||
defaultWidth: 1.05
|
||||
stationRecordsHidden: true
|
||||
|
||||
- type: bodyType
|
||||
id: MobAbductorSprites
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue