[Feat] War crime console (#144)
* ADD: Icons, New component. Надо будет рефакторнуть худы в одну систему * Some govno ebanoe * ui * Some govno * UI and UI lol * Dermo again * ы * Добавлена система консоли. Надо добавить манипуляцию с рекордами и сохранение крим. записей на сервер. Я пометил в туду * Added functional for Criminal Records UI * Дропаю это говно * Рабочая версия крим консоли * Fuull functional * Added radio * Arrest info feature * improve ui * another names * New texturem Sprite viewer * fix small names * Added login menu * Final fix. * ох * Убрал логгеры * fix Comments and Access to proto * moved dummy code, removed qustyions * added disposer() when window was close * Small fixes * Removed comments. Added DNA check for CriminalityHud * Small lol --------- Co-authored-by: DocNITE <docnite0530@gmail.com>
@@ -1,10 +1,13 @@
|
||||
using Content.Client.White.EntityCrimeRecords;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Mindshield.Components;
|
||||
using Content.Shared.Overlays;
|
||||
using Content.Shared.PDA;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Content.Shared.White.CriminalRecords;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
@@ -13,6 +16,11 @@ public sealed class ShowSecurityIconsSystem : EquipmentHudSystem<ShowSecurityIco
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeMan = default!;
|
||||
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
|
||||
// WD EDIT START
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
[Dependency] private readonly ShowCrimeRecordsSystem _parentSystem = default!;
|
||||
// WD EDIT END
|
||||
|
||||
[ValidatePrototypeId<StatusIconPrototype>]
|
||||
private const string JobIconForNoId = "JobIconNoId";
|
||||
@@ -74,8 +82,82 @@ public sealed class ShowSecurityIconsSystem : EquipmentHudSystem<ShowSecurityIco
|
||||
result.Add(icon);
|
||||
}
|
||||
|
||||
// Add arrest icons here, WYCI.
|
||||
// WD EDIT START
|
||||
if (!GetRecord(uid, out var type))
|
||||
return result;
|
||||
|
||||
var protoId = type switch
|
||||
{
|
||||
EnumCriminalRecordType.Discharged => "CriminalRecordIconDischarged",
|
||||
EnumCriminalRecordType.Incarcerated => "CriminalRecordIconIncarcerated",
|
||||
EnumCriminalRecordType.Parolled => "CriminalRecordIconParolled",
|
||||
EnumCriminalRecordType.Suspected => "CriminalRecordIconSuspected",
|
||||
EnumCriminalRecordType.Wanted => "CriminalRecordIconWanted",
|
||||
_ => "CriminalRecordIconReleased"
|
||||
};
|
||||
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>(protoId, out var recordIcon))
|
||||
result.Add(recordIcon);
|
||||
// WD EDIT END
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// WD EDIT START
|
||||
private bool GetRecord(EntityUid uid, out EnumCriminalRecordType type)
|
||||
{
|
||||
if (!_entManager.TryGetComponent(uid, out MetaDataComponent? meta))
|
||||
{
|
||||
type = EnumCriminalRecordType.Released;
|
||||
return false;
|
||||
}
|
||||
|
||||
var serverList = _entManager.EntityQuery<CriminalRecordsServerComponent>();
|
||||
foreach (var server in serverList)
|
||||
{
|
||||
// if all good - check avaible records
|
||||
foreach (var (key, info) in server.Cache)
|
||||
{
|
||||
// Check id
|
||||
if (_inventorySystem.TryGetSlotEntity(uid, "id", out var idUid))
|
||||
{
|
||||
// PDA
|
||||
if (_entManager.TryGetComponent(idUid, out PdaComponent? pda) &&
|
||||
_entManager.TryGetComponent(pda.ContainedId, out IdCardComponent? idCard))
|
||||
{
|
||||
if (idCard.FullName == info.StationRecord.Name &&
|
||||
idCard.JobTitle == info.StationRecord.JobTitle)
|
||||
{
|
||||
type = info.CriminalType;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// ID Card
|
||||
if (_entManager.TryGetComponent(idUid, out IdCardComponent? id))
|
||||
{
|
||||
idCard = id;
|
||||
if (idCard.FullName == info.StationRecord.Name &&
|
||||
idCard.JobTitle == info.StationRecord.JobTitle)
|
||||
{
|
||||
type = info.CriminalType;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check DNA (Dirty Nanotrasen tehnology lol)
|
||||
// And yeah, he can't check - is pulled mask or not
|
||||
// it's only Content.Server logic, idk hot it impl to Content.Client
|
||||
if (_parentSystem.CanIdentityName(uid) != meta.EntityName)
|
||||
continue;
|
||||
if (meta.EntityName != info.StationRecord.Name)
|
||||
continue;
|
||||
type = info.CriminalType;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
type = EnumCriminalRecordType.Released;
|
||||
return false;
|
||||
}
|
||||
// WD EDIT END
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<controls:RecordCard xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
xmlns:controls="clr-namespace:Content.Client.White.CriminalRecords.UI.Controls"
|
||||
x:Class="Content.Client.White.CriminalRecords.UI.Controls.RecordCard">
|
||||
|
||||
<BoxContainer Name="MainContainer" Orientation="Vertical" Margin="10 10 10 10">
|
||||
<!-- Header line -->
|
||||
<PanelContainer
|
||||
Name="SideLineElement"
|
||||
Access="Public"
|
||||
StyleClasses="PDABackground"
|
||||
MinHeight="25"
|
||||
VerticalExpand="False"
|
||||
HorizontalExpand="True"
|
||||
Margin="0 0 0 -5"/>
|
||||
<!-- Header info -->
|
||||
<PanelContainer VerticalExpand="True" HorizontalExpand="True" >
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#1e1c29" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Orientation="Vertical"
|
||||
VerticalExpand="True"
|
||||
Margin="5">
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<SpriteView Name="ViewIcon"
|
||||
Access="Public"
|
||||
Scale="2 2"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"/>
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalAlignment="Center" Margin="10 0 0 0">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Access="Public" Name="CharacterNameLabel" Text="empty-element" StyleClasses="LabelKeyText" HorizontalExpand="True"/>
|
||||
<TextureRect Access="Public" Name="JobIcon" TextureScale="3 3" VerticalAlignment="Center" HorizontalAlignment="Left"/>
|
||||
</BoxContainer>
|
||||
<customControls:HSeparator StyleClasses="LowDivider" Margin="0 5 0 5"/>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<!-- <RichTextLabel Access="Public" Name="StatusLabel" HorizontalAlignment="Left" StyleClasses="LabelSubText" HorizontalExpand="True"/> -->
|
||||
<OptionButton Access="Public" Name="StatusOption" MinWidth="120"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Main Info -->
|
||||
<Control MinHeight="5"></Control>
|
||||
<RichTextLabel Access="Public" Name="DetailLabel" HorizontalExpand="True" StyleClasses="LabelSubText" Margin="10 0 0 0"></RichTextLabel>
|
||||
<Control MinHeight="5"></Control>
|
||||
<BoxContainer Orientation="Vertical" Margin="10 0 0 0">
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<PanelContainer VerticalExpand="True" HorizontalExpand="True">
|
||||
<Label Text="{Loc 'criminal-detail-info'}" HorizontalAlignment="Left"/>
|
||||
<TextureButton Access="Public" Name="EditReason"
|
||||
TexturePath="/Textures/Interface/pencil.png"
|
||||
HorizontalAlignment="Right"></TextureButton>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
<customControls:HSeparator StyleClasses="LowDivider" Margin="0 0 0 10"/>
|
||||
</BoxContainer>
|
||||
<!-- Reason info -->
|
||||
<RichTextLabel Visible="True" Access="Public" Name="ReasonWritten" HorizontalExpand="True" VerticalExpand="True"
|
||||
MinHeight="100" Margin="10 0 0 0"/>
|
||||
<PanelContainer Visible="False" Access="Public" Name="InputContainer"
|
||||
StyleClasses="TransparentBorderedWindowPanel" MinHeight="100" Margin="10 0 0 0"
|
||||
VerticalAlignment="Stretch" VerticalExpand="True" HorizontalExpand="True">
|
||||
<TextEdit Name="Input" Access="Public" VerticalExpand="True" HorizontalExpand="True" />
|
||||
</PanelContainer>
|
||||
<BoxContainer Orientation="Vertical" Margin="10 0 0 0">
|
||||
<customControls:HSeparator StyleClasses="LowDivider" Margin="0 0 0 10"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
</controls:RecordCard>
|
||||
@@ -0,0 +1,88 @@
|
||||
using Content.Shared.StationRecords;
|
||||
using Content.Shared.White.CriminalRecords;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.White.CriminalRecords.UI.Controls;
|
||||
|
||||
[GenerateTypedNameReferences, Virtual]
|
||||
public partial class RecordCard : Control
|
||||
{
|
||||
private FormattedMessage _description = new FormattedMessage();
|
||||
public bool _canDoEvent = false;
|
||||
|
||||
public Action<string>? OnTextBindDown;
|
||||
|
||||
public RecordCard()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
StatusOption.AddItem(Loc.GetString("criminal-status-released"), (int)EnumCriminalRecordType.Released);
|
||||
StatusOption.AddItem(Loc.GetString("criminal-status-discharged"), (int)EnumCriminalRecordType.Discharged);
|
||||
StatusOption.AddItem(Loc.GetString("criminal-status-parolled"), (int)EnumCriminalRecordType.Parolled);
|
||||
StatusOption.AddItem(Loc.GetString("criminal-status-suspected"), (int)EnumCriminalRecordType.Suspected);
|
||||
StatusOption.AddItem(Loc.GetString("criminal-status-wanted"), (int)EnumCriminalRecordType.Wanted);
|
||||
StatusOption.AddItem(Loc.GetString("criminal-status-incarcerated"), (int)EnumCriminalRecordType.Incarcerated);
|
||||
|
||||
Input.OnKeyBindDown += args =>
|
||||
{
|
||||
if (args.Function == EngineKeyFunctions.TextSubmit)
|
||||
{
|
||||
UpdateReasonController();
|
||||
args.Handle();
|
||||
}
|
||||
};
|
||||
|
||||
EditReason.OnPressed += args =>
|
||||
{
|
||||
UpdateReasonController();
|
||||
};
|
||||
}
|
||||
|
||||
public void InitializeStatusOption(StationRecordKey key, Dictionary<StationRecordKey, CriminalRecordInfo> cache)
|
||||
{
|
||||
if (cache != null)
|
||||
{
|
||||
foreach (var (kkey, info) in cache)
|
||||
{
|
||||
if (kkey.Id == key.Id)
|
||||
{
|
||||
StatusOption.SelectId((int) info.CriminalType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateReasonController()
|
||||
{
|
||||
if (ReasonWritten.Visible)
|
||||
{
|
||||
var text = ReasonWritten.GetMessage();
|
||||
ReasonWritten.SetMessage("");
|
||||
ReasonWritten.Visible = false;
|
||||
if (text != null)
|
||||
{
|
||||
Input.CursorPosition = new TextEdit.CursorPos();
|
||||
Input.InsertAtCursor(text);
|
||||
}
|
||||
InputContainer.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var text = Rope.Collapse(Input.TextRope);
|
||||
OnTextBindDown?.Invoke(text);
|
||||
Input.TextRope = Rope.Leaf.Empty;
|
||||
Input.CursorPosition = new TextEdit.CursorPos(0, TextEdit.LineBreakBias.Top);
|
||||
// close input and open label
|
||||
InputContainer.Visible = false;
|
||||
// for label
|
||||
ReasonWritten.SetMessage(text);
|
||||
ReasonWritten.Visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<!-- close.svg.192dpi -->
|
||||
<controls:RecordIconButton xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:Content.Client.White.CriminalRecords.UI.Controls"
|
||||
x:Class="Content.Client.White.CriminalRecords.UI.Controls.RecordIconButton">
|
||||
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Button
|
||||
Name="Controller"
|
||||
Access="Public"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="False"
|
||||
ToolTip="foobar"
|
||||
TooltipDelay="0.25">
|
||||
<BoxContainer Orientation="Horizontal" >
|
||||
<TextureRect Access="Public" Name="LIcon"
|
||||
MaxWidth="24"
|
||||
MaxHeight="24"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Left" Stretch="KeepAspectCentered" />
|
||||
<RichTextLabel Access="Public"
|
||||
HorizontalExpand="True"
|
||||
Name="LLabel"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Left"/>
|
||||
</BoxContainer>
|
||||
</Button>
|
||||
</BoxContainer>
|
||||
</controls:RecordIconButton>
|
||||
@@ -0,0 +1,36 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.White.CriminalRecords.UI.Controls;
|
||||
|
||||
[GenerateTypedNameReferences, Virtual]
|
||||
public partial class RecordIconButton : Control
|
||||
{
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
|
||||
public string Icon
|
||||
{
|
||||
set
|
||||
{
|
||||
var path = new ResPath(value); // /Textures/Interface/VerbIcons/close.svg.192dpi.png
|
||||
var specifier = new SpriteSpecifier.Texture(path);
|
||||
LIcon.Texture = specifier.Frame0();
|
||||
}
|
||||
}
|
||||
|
||||
public string Label
|
||||
{
|
||||
set => LLabel.SetMessage(value);
|
||||
}
|
||||
|
||||
public RecordIconButton()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<controls:RecordItem xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:Content.Client.White.CriminalRecords.UI.Controls"
|
||||
x:Class="Content.Client.White.CriminalRecords.UI.Controls.RecordItem">
|
||||
|
||||
<BoxContainer MaxHeight="25" Orientation="Horizontal">
|
||||
<PanelContainer
|
||||
Name="SideLineElement"
|
||||
Access="Public"
|
||||
StyleClasses="PDABackground"
|
||||
MaxWidth="10"
|
||||
VerticalExpand="False"
|
||||
HorizontalExpand="False"
|
||||
Margin="0 0 -5 0"/>
|
||||
<Button
|
||||
Name="ButtonElement"
|
||||
Access="Public"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="False"
|
||||
ToolTip="foobar"
|
||||
TooltipDelay="0.25">
|
||||
<BoxContainer Orientation="Horizontal" Margin="0">
|
||||
<RichTextLabel Access="Public"
|
||||
HorizontalExpand="True"
|
||||
Name="NameLabel"
|
||||
StyleClasses="LabelSubText"
|
||||
VerticalAlignment="Center"/>
|
||||
</BoxContainer>
|
||||
</Button>
|
||||
</BoxContainer>
|
||||
</controls:RecordItem>
|
||||
@@ -0,0 +1,14 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.White.CriminalRecords.UI.Controls;
|
||||
|
||||
[GenerateTypedNameReferences, Virtual]
|
||||
public partial class RecordItem : Control
|
||||
{
|
||||
public RecordItem()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.StationRecords;
|
||||
using Content.Shared.White.CriminalRecords;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.White.CriminalRecords.UI;
|
||||
|
||||
public sealed class CriminalRecordsBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private CriminalRecordsWindow? _window = default!;
|
||||
|
||||
public CriminalRecordsBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new();
|
||||
_window.OnKeySelected += OnKeySelected;
|
||||
_window.OnStatusSelected += OnStatusSelected;
|
||||
_window.OnTextBindDown += OnTextEntered;
|
||||
_window.LogOutButton.Controller.OnPressed += _ =>
|
||||
{
|
||||
SendMessage(new ItemSlotButtonPressedEvent(CriminalRecordsConsoleComponent.IdSlotId));
|
||||
};
|
||||
_window.LogInButton.Controller.OnPressed += _ =>
|
||||
{
|
||||
SendMessage(new ItemSlotButtonPressedEvent(CriminalRecordsConsoleComponent.IdSlotId));
|
||||
};
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
private void OnKeySelected(StationRecordKey key)
|
||||
{
|
||||
SendMessage(new SelectCriminalRecord(key));
|
||||
}
|
||||
|
||||
private void OnStatusSelected(StationRecordKey key, CriminalRecordInfo status)
|
||||
{
|
||||
SendMessage(new SelectCriminalStatus(key, status));
|
||||
}
|
||||
|
||||
private void OnTextEntered(StationRecordKey key, string text)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
SendMessage(new SelectCriminalReason(key, text));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not CriminalRecordsConsoleBuiState cast)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_window?.UpdateState(cast);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
_window?.Close();
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
xmlns:controls="clr-namespace:Content.Client.White.CriminalRecords.UI.Controls"
|
||||
xmlns:uicontrols="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc 'criminal-console-name'}"
|
||||
MinSize="625 500"
|
||||
Resizable="False">
|
||||
<BoxContainer Access="Public" Name="NonAccessContent" Orientation="Vertical" VerticalAlignment="Center" VerticalExpand="True" HorizontalExpand="True">
|
||||
<BoxContainer Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<RichTextLabel Access="Public" HorizontalAlignment="Center" Name="LoginHint" HorizontalExpand="True" VerticalExpand="True"
|
||||
MaxWidth="400" Margin="10 0 0 0"/>
|
||||
<controls:RecordIconButton
|
||||
Name="LogInButton"
|
||||
Access="Public"
|
||||
HorizontalAlignment="Center"
|
||||
Icon="/Textures/Interface/VerbIcons/close.svg.192dpi.png"
|
||||
Label="{Loc 'criminal-login-in'}"
|
||||
Margin="10 0 10 0">
|
||||
</controls:RecordIconButton>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
<BoxContainer Access="Public" Name="MainContent" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
|
||||
<uicontrols:StripeBack HasBottomEdge="True" HasMargins="False" HorizontalExpand="True">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<RichTextLabel Access="Public"
|
||||
HorizontalExpand="True"
|
||||
Name="UserLabel"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="10 0 10 0"/>
|
||||
<controls:RecordIconButton
|
||||
Name="LogOutButton"
|
||||
Access="Public"
|
||||
HorizontalAlignment="Right"
|
||||
Icon="/Textures/Interface/VerbIcons/close.svg.192dpi.png"
|
||||
Label="{Loc 'criminal-login-out'}"
|
||||
Margin="10 0 10 0">
|
||||
</controls:RecordIconButton>
|
||||
</BoxContainer>
|
||||
</uicontrols:StripeBack>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" VerticalExpand="True">
|
||||
<!-- Persons list (chars) -->
|
||||
<BoxContainer Orientation="Vertical"
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="True"
|
||||
HorizontalAlignment="Left"
|
||||
SizeFlagsStretchRatio="2"
|
||||
Margin="10 0 10 10"
|
||||
MinWidth="175"
|
||||
MaxWidth="175">
|
||||
<Label Text="{Loc 'criminal-console-list'}" HorizontalAlignment="Center"/>
|
||||
<customControls:HSeparator StyleClasses="LowDivider" Margin="0 0 0 10"/>
|
||||
<PanelContainer VerticalExpand="True">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<ScrollContainer
|
||||
HScrollEnabled="False"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="True">
|
||||
<BoxContainer
|
||||
Name="RecordsListContainer"
|
||||
Access="Public"
|
||||
Orientation="Vertical"
|
||||
VerticalExpand="True">
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
<!-- Character info -->
|
||||
<BoxContainer Orientation="Vertical"
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="True"
|
||||
SizeFlagsStretchRatio="3"
|
||||
Margin="0 0 10 10">
|
||||
<PanelContainer VerticalExpand="True" MinSize="0 200">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<ScrollContainer
|
||||
HScrollEnabled="False"
|
||||
HorizontalExpand="True"
|
||||
SizeFlagsStretchRatio="2"
|
||||
VerticalExpand="True">
|
||||
<BoxContainer
|
||||
Name="RecordCardContainer"
|
||||
Access="Public"
|
||||
MinSize="100 256"
|
||||
Orientation="Vertical"
|
||||
SizeFlagsStretchRatio="2"
|
||||
VerticalExpand="True">
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
@@ -0,0 +1,291 @@
|
||||
using System.Linq;
|
||||
using Content.Client.Humanoid;
|
||||
using Content.Client.Inventory;
|
||||
using Content.Client.White.CriminalRecords.UI.Controls;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.CrewManifest;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Radio.Components;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.StationRecords;
|
||||
using Content.Shared.White.CriminalRecords;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.White.CriminalRecords.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class CriminalRecordsWindow : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public Action<StationRecordKey>? OnKeySelected;
|
||||
public Action<StationRecordKey, string>? OnTextBindDown;
|
||||
public Action<StationRecordKey, CriminalRecordInfo>? OnStatusSelected;
|
||||
|
||||
public CriminalRecordsWindow() : base()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public void UpdateState(CriminalRecordsConsoleBuiState state)
|
||||
{
|
||||
// Check access AllAccess Security
|
||||
var jobs = _prototypeManager.EnumeratePrototypes<JobPrototype>();
|
||||
var canAccess = state.IsAllowed;
|
||||
// Check if exists card or not
|
||||
if (state.ContainedId == null || !canAccess)
|
||||
{
|
||||
var messageHint = new FormattedMessage();
|
||||
messageHint.AddMarkup(Loc.GetString("criminal-login-hint", ("name", Loc.GetString("criminal-login-in"))));
|
||||
messageHint.AddMarkup("\n\n");
|
||||
messageHint.AddMarkup(Loc.GetString("criminal-login-warn"));
|
||||
LoginHint.SetMessage(messageHint);
|
||||
MainContent.Visible = false;
|
||||
NonAccessContent.Visible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
MainContent.Visible = true;
|
||||
NonAccessContent.Visible = false;
|
||||
|
||||
// Init header panel
|
||||
UserLabel.SetMessage(Loc.GetString("criminal-login-info",
|
||||
("user", (state.ContainedId.FullName ?? string.Empty) + ", " +
|
||||
(state.ContainedId.JobTitle ?? string.Empty) )));
|
||||
|
||||
// Make crew list
|
||||
Populate(state, state.RecordListing);
|
||||
|
||||
// Make card
|
||||
if (state is { SelectedKey: not null, Record: not null })
|
||||
{
|
||||
CreateRecordCard(state, state.SelectedKey.Value, state.Record);
|
||||
}
|
||||
}
|
||||
|
||||
public void Populate(CriminalRecordsConsoleBuiState State, Dictionary<StationRecordKey, string>? RecordListing)
|
||||
{
|
||||
if (RecordListing == null)
|
||||
return;
|
||||
|
||||
// clear govno from list
|
||||
RecordsListContainer.RemoveAllChildren();
|
||||
|
||||
foreach (var (recordKey, name) in RecordListing)
|
||||
{
|
||||
var element = CreateRecordItem(State, recordKey, name);
|
||||
element.ButtonElement.OnPressed += _ =>
|
||||
{
|
||||
OnKeySelected?.Invoke(recordKey);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private CriminalRecordInfo? GetRecord(StationRecordKey Key, Dictionary<StationRecordKey, CriminalRecordInfo> Cache)
|
||||
{
|
||||
foreach (var (key, info) in Cache)
|
||||
{
|
||||
if (Key.Id == key.Id)
|
||||
{
|
||||
return info;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Color GetColor(StationRecordKey Key, Dictionary<StationRecordKey, CriminalRecordInfo> Cache)
|
||||
{
|
||||
var info = GetRecord(Key, Cache);
|
||||
if (info == null)
|
||||
return new Color(0,0,0);
|
||||
|
||||
switch (info.CriminalType)
|
||||
{
|
||||
case EnumCriminalRecordType.Released:
|
||||
return new Color(14, 106, 254);
|
||||
case EnumCriminalRecordType.Discharged:
|
||||
return new Color(14,106,254);
|
||||
case EnumCriminalRecordType.Parolled:
|
||||
return new Color(151,196,66);
|
||||
case EnumCriminalRecordType.Suspected:
|
||||
return new Color(217,126,35);
|
||||
case EnumCriminalRecordType.Wanted:
|
||||
return new Color(190, 50 ,50);
|
||||
case EnumCriminalRecordType.Incarcerated:
|
||||
return new Color(196,164,114);
|
||||
}
|
||||
return new Color(0,0,0);
|
||||
}
|
||||
|
||||
private RecordItem CreateRecordItem(CriminalRecordsConsoleBuiState State, StationRecordKey Key, string Name)
|
||||
{
|
||||
var record = new RecordItem();
|
||||
record.VerticalAlignment = Control.VAlignment.Top;
|
||||
if (State.Cache != null && GetRecord(Key, State.Cache) != null)
|
||||
{
|
||||
record.SideLineElement.ModulateSelfOverride = GetColor(Key, State.Cache);
|
||||
}
|
||||
else
|
||||
{
|
||||
record.SideLineElement.ModulateSelfOverride = new Color(0, 0 ,0);
|
||||
}
|
||||
record.NameLabel.SetMessage(Name);
|
||||
// append element into list
|
||||
RecordsListContainer.AddChild(record);
|
||||
// result
|
||||
return record;
|
||||
}
|
||||
|
||||
private RecordCard CreateRecordCard(CriminalRecordsConsoleBuiState State, StationRecordKey Key, GeneralStationRecord Record)
|
||||
{
|
||||
var card = new RecordCard();
|
||||
// set color
|
||||
if (State.Cache != null && GetRecord(Key, State.Cache) != null)
|
||||
{
|
||||
card.SideLineElement.ModulateSelfOverride = GetColor(Key, State.Cache);
|
||||
}
|
||||
else
|
||||
{
|
||||
card.SideLineElement.ModulateSelfOverride = new Color(0, 0 ,0);
|
||||
}
|
||||
// name
|
||||
card.CharacterNameLabel.Text = Record.Name + ", " + Record.JobTitle;
|
||||
// job icon
|
||||
var path = new ResPath("/Textures/Interface/Misc/job_icons.rsi");
|
||||
_resourceCache.TryGetResource(path, out RSIResource? rsi);
|
||||
|
||||
if (rsi != null)
|
||||
{
|
||||
if (rsi.RSI.TryGetState(Record.JobIcon, out _))
|
||||
{
|
||||
var specifier = new SpriteSpecifier.Rsi(path, Record.JobIcon);
|
||||
card.JobIcon.Texture = specifier.Frame0();
|
||||
}
|
||||
else if (rsi.RSI.TryGetState("Unknown", out _))
|
||||
{
|
||||
var specifier = new SpriteSpecifier.Rsi(path, "Unknown");
|
||||
card.JobIcon.Texture = specifier.Frame0();
|
||||
}
|
||||
}
|
||||
// status icon
|
||||
card.ViewIcon.SetEntity(CreateCharacterDummy(Record));
|
||||
// info
|
||||
var dnaInfo = "";
|
||||
if (Record.DNA != null)
|
||||
dnaInfo = Record.DNA;
|
||||
|
||||
var fingerprintInfo = "";
|
||||
if (Record.Fingerprint != null)
|
||||
fingerprintInfo = Record.Fingerprint;
|
||||
|
||||
var message = new FormattedMessage();
|
||||
message.AddMarkup(Loc.GetString("criminal-dna-name"));
|
||||
message.AddMarkup("\n");
|
||||
message.AddMarkup(Loc.GetString("criminal-dna-desc",
|
||||
("color", new Color(171,129,222).ToHex()), ("info", dnaInfo) ));
|
||||
message.AddMarkup("\n");
|
||||
message.AddMarkup(Loc.GetString("criminal-fingerprint-name"));
|
||||
message.AddMarkup("\n");
|
||||
message.AddMarkup(Loc.GetString("criminal-fingerprint-desc",
|
||||
("color", new Color(171,129,222).ToHex()), ("info", fingerprintInfo) ));
|
||||
card.DetailLabel.SetMessage(message);
|
||||
// clear and aapend
|
||||
RecordCardContainer.DisposeAllChildren();
|
||||
RecordCardContainer.AddChild(card);
|
||||
// some shit (like change status)
|
||||
card.StatusOption.OnItemSelected += eventArgs =>
|
||||
{
|
||||
if (!card._canDoEvent)
|
||||
return;
|
||||
|
||||
var record = new CriminalRecordInfo(Record, (EnumCriminalRecordType)eventArgs.Id, card.ReasonWritten.GetMessage() ?? string.Empty);
|
||||
OnStatusSelected?.Invoke(Key, record);
|
||||
card.StatusOption.SelectId(eventArgs.Id);
|
||||
};
|
||||
// init status option
|
||||
if (State.Cache != null)
|
||||
{
|
||||
card.InitializeStatusOption(Key, State.Cache);
|
||||
card._canDoEvent = true;
|
||||
}
|
||||
// init reason text
|
||||
if (State.Cache != null)
|
||||
{
|
||||
var criminalInfo = GetRecord(Key, State.Cache);
|
||||
if (criminalInfo != null)
|
||||
card.ReasonWritten.SetMessage(criminalInfo.Reason);
|
||||
}
|
||||
|
||||
card.OnTextBindDown += args =>
|
||||
{
|
||||
OnTextBindDown?.Invoke(Key, args);
|
||||
};
|
||||
// rsult
|
||||
return card;
|
||||
}
|
||||
|
||||
private EntityUid CreateCharacterDummy(GeneralStationRecord Record)
|
||||
{
|
||||
IEntityManager entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
IPrototypeManager prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
HumanoidAppearanceSystem appearanceSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<HumanoidAppearanceSystem>();
|
||||
|
||||
var profile = Record.Profile ?? new HumanoidCharacterProfile();
|
||||
var _previewDummy = entityManager.SpawnEntity(prototypeManager.Index<SpeciesPrototype>(profile.Species).DollPrototype, MapCoordinates.Nullspace);
|
||||
appearanceSystem.LoadProfile(_previewDummy, profile);
|
||||
GiveDummyJobClothes(_previewDummy, Record.JobPrototype, profile);
|
||||
|
||||
return _previewDummy;
|
||||
}
|
||||
|
||||
private void GiveDummyJobClothes(EntityUid dummy, string jobPrototype, HumanoidCharacterProfile profile)
|
||||
{
|
||||
IEntityManager entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
IPrototypeManager prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
ClientInventorySystem inventorySystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ClientInventorySystem>();
|
||||
|
||||
// ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract (what is resharper smoking?)
|
||||
var job = prototypeManager.Index<JobPrototype>(jobPrototype ?? SharedGameTicker.FallbackOverflowJob);
|
||||
|
||||
if (job.StartingGear != null && inventorySystem.TryGetSlots(dummy, out var slots))
|
||||
{
|
||||
var gear = prototypeManager.Index<StartingGearPrototype>(job.StartingGear);
|
||||
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
var itemType = gear.GetGear(slot.Name, profile);
|
||||
if (inventorySystem.TryUnequip(dummy, slot.Name, out var unequippedItem, true, true))
|
||||
{
|
||||
entityManager.DeleteEntity(unequippedItem.Value);
|
||||
}
|
||||
|
||||
if (itemType != string.Empty)
|
||||
{
|
||||
var item = entityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
|
||||
inventorySystem.TryEquip(dummy, item, slot.Name, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GetAccess()
|
||||
{
|
||||
//_accessReader.FindAccessTags(item).ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.PDA;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Roles;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using Content.Shared.White.CriminalRecords;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Client.White.EntityCrimeRecords;
|
||||
|
||||
public sealed class EntityCrimeRecordsOverlay : Overlay
|
||||
{
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly SharedTransformSystem _transform;
|
||||
private readonly IPrototypeManager _prototypeManager;
|
||||
private readonly InventorySystem _inventorySystem;
|
||||
private readonly ShaderInstance _shader;
|
||||
private readonly ShowCrimeRecordsSystem _parentSystem;
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
|
||||
|
||||
public EntityCrimeRecordsOverlay(
|
||||
IEntityManager entManager,
|
||||
IPrototypeManager protoManager,
|
||||
InventorySystem inventorySystem,
|
||||
ShowCrimeRecordsSystem showCrimSystem)
|
||||
{
|
||||
_entManager = entManager;
|
||||
_prototypeManager = protoManager;
|
||||
_inventorySystem = inventorySystem;
|
||||
_parentSystem = showCrimSystem;
|
||||
_transform = _entManager.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
|
||||
_shader = protoManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var handle = args.WorldHandle;
|
||||
var rotation = args.Viewport.Eye?.Rotation ?? Angle.Zero;
|
||||
var spriteQuery = _entManager.GetEntityQuery<SpriteComponent>();
|
||||
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
const float scale = 1f;
|
||||
var scaleMatrix = Matrix3.CreateScale(new Vector2(scale, scale));
|
||||
var rotationMatrix = Matrix3.CreateRotation(-rotation);
|
||||
handle.UseShader(_shader);
|
||||
// da pizda
|
||||
this.ZIndex = this.ZIndex -= 1;
|
||||
|
||||
foreach (var hum in _entManager.EntityQuery<HumanoidAppearanceComponent>(true))
|
||||
{
|
||||
if (!xformQuery.TryGetComponent(hum.Owner, out var xform) ||
|
||||
xform.MapID != args.MapId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var worldPosition = _transform.GetWorldPosition(xform);
|
||||
var worldMatrix = Matrix3.CreateTranslation(worldPosition);
|
||||
|
||||
Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld);
|
||||
Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty);
|
||||
|
||||
handle.SetTransform(matty);
|
||||
|
||||
if (GetRecord(hum.Owner, args.MapId, out var criminalType))
|
||||
{
|
||||
var icon = "released";
|
||||
switch (criminalType)
|
||||
{
|
||||
case EnumCriminalRecordType.Released:
|
||||
icon = "released";
|
||||
break;
|
||||
case EnumCriminalRecordType.Discharged:
|
||||
icon = "discharged";
|
||||
break;
|
||||
case EnumCriminalRecordType.Parolled:
|
||||
icon = "parolled";
|
||||
break;
|
||||
case EnumCriminalRecordType.Suspected:
|
||||
icon = "suspected";
|
||||
break;
|
||||
case EnumCriminalRecordType.Wanted:
|
||||
icon = "wanted";
|
||||
break;
|
||||
case EnumCriminalRecordType.Incarcerated:
|
||||
icon = "incarcerated";
|
||||
break;
|
||||
}
|
||||
|
||||
var sprite_icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/White/Interface/records.rsi"), icon);
|
||||
var _iconTexture = _entManager.EntitySysManager.GetEntitySystem<SpriteSystem>().Frame0(sprite_icon);
|
||||
|
||||
float yOffset;
|
||||
float xOffset;
|
||||
if (spriteQuery.TryGetComponent(hum.Owner, out var sprite))
|
||||
{
|
||||
yOffset = sprite.Bounds.Height + 7f - 7f; //sprite.Bounds.Height + 7f;
|
||||
xOffset = sprite.Bounds.Width - 17f; //sprite.Bounds.Width + 7f;
|
||||
}
|
||||
else
|
||||
{
|
||||
yOffset = 1f;
|
||||
xOffset = 1f;
|
||||
}
|
||||
|
||||
// Position above the entity (we've already applied the matrix transform to the entity itself)
|
||||
// Offset by the texture size for every do_after we have.
|
||||
var position = new Vector2(xOffset / EyeManager.PixelsPerMeter,
|
||||
yOffset / EyeManager.PixelsPerMeter);
|
||||
|
||||
// Draw the underlying bar texture
|
||||
if (sprite != null && !sprite.ContainerOccluded)
|
||||
handle.DrawTexture(_iconTexture, position);
|
||||
}
|
||||
}
|
||||
|
||||
handle.UseShader(null);
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
}
|
||||
|
||||
private bool GetRecord(EntityUid uid, MapId mapId, out EnumCriminalRecordType type)
|
||||
{
|
||||
if (!_entManager.TryGetComponent(uid, out MetaDataComponent? meta))
|
||||
{
|
||||
type = EnumCriminalRecordType.Released;
|
||||
return false;
|
||||
}
|
||||
|
||||
var serverList = _entManager.EntityQuery<CriminalRecordsServerComponent>();
|
||||
foreach (var server in serverList)
|
||||
{
|
||||
// if all good - check avaible records
|
||||
foreach (var (key, info) in server.Cache)
|
||||
{
|
||||
// Check id
|
||||
if (_inventorySystem.TryGetSlotEntity(uid, "id", out var idUid))
|
||||
{
|
||||
// PDA
|
||||
if (_entManager.TryGetComponent(idUid, out PdaComponent? pda) &&
|
||||
_entManager.TryGetComponent(pda.ContainedId, out IdCardComponent? idCard))
|
||||
{
|
||||
if (idCard.FullName == info.StationRecord.Name &&
|
||||
idCard.JobTitle == info.StationRecord.JobTitle)
|
||||
{
|
||||
type = info.CriminalType;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// ID Card
|
||||
if (_entManager.TryGetComponent(idUid, out IdCardComponent? id))
|
||||
{
|
||||
idCard = id;
|
||||
if (idCard.FullName == info.StationRecord.Name &&
|
||||
idCard.JobTitle == info.StationRecord.JobTitle)
|
||||
{
|
||||
type = info.CriminalType;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check DNA (Dirty Nanotrasen tehnology lol)
|
||||
// And yeah, he can't check - is pulled mask or not
|
||||
// it's only Content.Server logic, idk hot it impl to Content.Client
|
||||
if (_parentSystem.CanIdentityName(uid) != meta.EntityName)
|
||||
continue;
|
||||
if (meta.EntityName != info.StationRecord.Name)
|
||||
continue;
|
||||
type = info.CriminalType;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
type = EnumCriminalRecordType.Released;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using Content.Shared.White.EntityCrimeRecords;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.IdentityManagement.Components;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.Inventory;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client.White.EntityCrimeRecords
|
||||
{
|
||||
public sealed class ShowCrimeRecordsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
|
||||
private EntityCrimeRecordsOverlay _overlay = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ShowCrimeRecordsComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<ShowCrimeRecordsComponent, ComponentRemove>(OnRemove);
|
||||
SubscribeLocalEvent<ShowCrimeRecordsComponent, PlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<ShowCrimeRecordsComponent, PlayerDetachedEvent>(OnPlayerDetached);
|
||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
|
||||
|
||||
_overlay = new(EntityManager, _protoMan, _inventorySystem, this);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, ShowCrimeRecordsComponent component, ComponentInit args)
|
||||
{
|
||||
if (_player.LocalPlayer?.ControlledEntity == uid)
|
||||
{
|
||||
_overlayMan.AddOverlay(_overlay);
|
||||
}
|
||||
}
|
||||
private void OnRemove(EntityUid uid, ShowCrimeRecordsComponent component, ComponentRemove args)
|
||||
{
|
||||
if (_player.LocalPlayer?.ControlledEntity == uid)
|
||||
{
|
||||
_overlayMan.RemoveOverlay(_overlay);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPlayerAttached(EntityUid uid, ShowCrimeRecordsComponent component, PlayerAttachedEvent args)
|
||||
{
|
||||
_overlayMan.AddOverlay(_overlay);
|
||||
}
|
||||
|
||||
private void OnPlayerDetached(EntityUid uid, ShowCrimeRecordsComponent component, PlayerDetachedEvent args)
|
||||
{
|
||||
_overlayMan.RemoveOverlay(_overlay);
|
||||
}
|
||||
|
||||
private void OnRoundRestart(RoundRestartCleanupEvent args)
|
||||
{
|
||||
_overlayMan.RemoveOverlay(_overlay);
|
||||
}
|
||||
|
||||
public string CanIdentityName(EntityUid target)
|
||||
{
|
||||
var representation = GetIdentityRepresentation(target);
|
||||
var ev = new SeeIdentityAttemptEvent();
|
||||
|
||||
RaiseLocalEvent(target, ev);
|
||||
return representation.ToStringKnown(!ev.Cancelled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an 'identity representation' of an entity, with their true name being the entity name
|
||||
/// and their 'presumed name' and 'presumed job' being the name/job on their ID card, if they have one.
|
||||
/// </summary>
|
||||
private IdentityRepresentation GetIdentityRepresentation(EntityUid target,
|
||||
InventoryComponent? inventory=null,
|
||||
HumanoidAppearanceComponent? appearance=null)
|
||||
{
|
||||
var age = 18;
|
||||
var gender = Gender.Epicene;
|
||||
|
||||
// Always use their actual age and gender, since that can't really be changed by an ID.
|
||||
if (Resolve(target, ref appearance, false))
|
||||
{
|
||||
gender = appearance.Gender;
|
||||
age = appearance.Age;
|
||||
}
|
||||
|
||||
var trueName = Name(target);
|
||||
if (!Resolve(target, ref inventory, false))
|
||||
return new(trueName, gender, age.ToString(), string.Empty);
|
||||
|
||||
string? presumedJob = null;
|
||||
string? presumedName = null;
|
||||
|
||||
// If it didn't find a job, that's fine.
|
||||
return new IdentityRepresentation(trueName, gender, age.ToString(), presumedName, presumedJob);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -349,7 +349,7 @@ namespace Content.Server.Administration.Systems
|
||||
if (TryComp(item, out PdaComponent? pda) &&
|
||||
TryComp(pda.ContainedId, out StationRecordKeyStorageComponent? keyStorage) &&
|
||||
keyStorage.Key is { } key &&
|
||||
_stationRecords.TryGetRecord(key.OriginStation, key, out GeneralStationRecord? record))
|
||||
_stationRecords.TryGetRecord(GetEntity(key.OriginStation), key, out GeneralStationRecord? record))
|
||||
{
|
||||
if (TryComp(entity, out DnaComponent? dna) &&
|
||||
dna.DNA != record.DNA)
|
||||
@@ -363,7 +363,7 @@ namespace Content.Server.Administration.Systems
|
||||
continue;
|
||||
}
|
||||
|
||||
_stationRecords.RemoveRecord(key.OriginStation, key);
|
||||
_stationRecords.RemoveRecord(GetEntity(key.OriginStation), key);
|
||||
Del(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,20 +73,20 @@ public sealed class CrewManifestSystem : EntitySystem
|
||||
// wrt the amount of players readied up.
|
||||
private void AfterGeneralRecordCreated(AfterGeneralRecordCreatedEvent ev)
|
||||
{
|
||||
BuildCrewManifest(ev.Key.OriginStation);
|
||||
UpdateEuis(ev.Key.OriginStation);
|
||||
BuildCrewManifest(GetEntity(ev.Key.OriginStation));
|
||||
UpdateEuis(GetEntity(ev.Key.OriginStation));
|
||||
}
|
||||
|
||||
private void OnRecordModified(RecordModifiedEvent ev)
|
||||
{
|
||||
BuildCrewManifest(ev.Key.OriginStation);
|
||||
UpdateEuis(ev.Key.OriginStation);
|
||||
BuildCrewManifest(GetEntity(ev.Key.OriginStation));
|
||||
UpdateEuis(GetEntity(ev.Key.OriginStation));
|
||||
}
|
||||
|
||||
private void OnRecordRemoved(RecordRemovedEvent ev)
|
||||
{
|
||||
BuildCrewManifest(ev.Key.OriginStation);
|
||||
UpdateEuis(ev.Key.OriginStation);
|
||||
BuildCrewManifest(GetEntity(ev.Key.OriginStation));
|
||||
UpdateEuis(GetEntity(ev.Key.OriginStation));
|
||||
}
|
||||
|
||||
private void OnBoundUiClose(EntityUid uid, CrewManifestViewerComponent component, BoundUIClosedEvent ev)
|
||||
|
||||
@@ -72,14 +72,14 @@ public sealed class RenameCommand : IConsoleCommand
|
||||
{
|
||||
var origin = keyStorage.Key.Value.OriginStation;
|
||||
|
||||
if (recordsSystem.TryGetRecord<GeneralStationRecord>(origin,
|
||||
if (recordsSystem.TryGetRecord<GeneralStationRecord>(_entManager.GetEntity(origin),
|
||||
keyStorage.Key.Value,
|
||||
out var generalRecord))
|
||||
{
|
||||
generalRecord.Name = name;
|
||||
}
|
||||
|
||||
recordsSystem.Synchronize(origin);
|
||||
recordsSystem.Synchronize(_entManager.GetEntity(origin));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public sealed partial class StationRecordSet
|
||||
/// </summary>
|
||||
/// <param name="entry">Entry to add.</param>
|
||||
/// <typeparam name="T">Type of the entry that's being added.</typeparam>
|
||||
public StationRecordKey AddRecordEntry<T>(EntityUid station, T entry)
|
||||
public StationRecordKey AddRecordEntry<T>(NetEntity station, T entry)
|
||||
{
|
||||
if (entry == null)
|
||||
return StationRecordKey.Invalid;
|
||||
|
||||
@@ -224,7 +224,7 @@ public sealed class StationRecordsSystem : SharedStationRecordsSystem
|
||||
if (!Resolve(station, ref records))
|
||||
return StationRecordKey.Invalid;
|
||||
|
||||
return records.Records.AddRecordEntry(station, record);
|
||||
return records.Records.AddRecordEntry(GetNetEntity(station), record);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Radio.EntitySystems;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Server.StationRecords;
|
||||
using Content.Server.StationRecords.Systems;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.PDA;
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.StationRecords;
|
||||
using Content.Shared.White.CriminalRecords;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.White.CriminalRecords;
|
||||
|
||||
public sealed class CriminalRecordsConsoleSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
|
||||
[Dependency] private readonly StationSystem _stationSystem = default!;
|
||||
[Dependency] private readonly StationRecordsSystem _stationRecordsSystem = default!;
|
||||
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
|
||||
[Dependency] private readonly RadioSystem _radioSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<CriminalRecordsConsoleComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<CriminalRecordsConsoleComponent, ComponentRemove>(OnComponentRemove);
|
||||
SubscribeLocalEvent<CriminalRecordsConsoleComponent, BoundUIOpenedEvent>(UpdateUserInterface);
|
||||
SubscribeLocalEvent<CriminalRecordsConsoleComponent, EntInsertedIntoContainerMessage>(OnItemInserted);
|
||||
SubscribeLocalEvent<CriminalRecordsConsoleComponent, EntRemovedFromContainerMessage>(OnItemRemoved);
|
||||
SubscribeLocalEvent<CriminalRecordsConsoleComponent, SelectCriminalRecord>(OnKeySelected);
|
||||
SubscribeLocalEvent<CriminalRecordsConsoleComponent, SelectCriminalStatus>(OnStatusSelected);
|
||||
SubscribeLocalEvent<CriminalRecordsConsoleComponent, SelectCriminalReason>(OnReasonSelected);
|
||||
SubscribeLocalEvent<CriminalRecordsConsoleComponent, RecordModifiedEvent>(UpdateUserInterface);
|
||||
SubscribeLocalEvent<CriminalRecordsConsoleComponent, AfterGeneralRecordCreatedEvent>(UpdateUserInterface);
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, CriminalRecordsConsoleComponent component, ComponentInit args)
|
||||
{
|
||||
_itemSlotsSystem.AddItemSlot(uid, CriminalRecordsConsoleComponent.IdSlotId, component.IdSlot);
|
||||
}
|
||||
|
||||
private void OnComponentRemove(EntityUid uid, CriminalRecordsConsoleComponent component, ComponentRemove args)
|
||||
{
|
||||
_itemSlotsSystem.RemoveItemSlot(uid, component.IdSlot);
|
||||
}
|
||||
|
||||
private void UpdateUserInterface<T>(EntityUid uid, CriminalRecordsConsoleComponent component, T ev)
|
||||
{
|
||||
UpdateUserInterface(uid, component);
|
||||
}
|
||||
|
||||
private void OnItemInserted(EntityUid uid, CriminalRecordsConsoleComponent component, EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (args.Container.ID == CriminalRecordsConsoleComponent.IdSlotId)
|
||||
component.ContainedID = CompOrNull<IdCardComponent>(args.Entity);
|
||||
|
||||
UpdateUserInterface(uid, component);
|
||||
}
|
||||
|
||||
private void OnItemRemoved(EntityUid uid, CriminalRecordsConsoleComponent component, EntRemovedFromContainerMessage args)
|
||||
{
|
||||
if (args.Container.ID == component.IdSlot.ID)
|
||||
component.ContainedID = null;
|
||||
|
||||
UpdateUserInterface(uid, component);
|
||||
}
|
||||
|
||||
private void OnKeySelected(EntityUid uid, CriminalRecordsConsoleComponent component,
|
||||
SelectCriminalRecord msg)
|
||||
{
|
||||
component.ActiveKey = msg.SelectedKey;
|
||||
UpdateUserInterface(uid, component);
|
||||
}
|
||||
|
||||
private void OnReasonSelected(EntityUid uid, CriminalRecordsConsoleComponent component,
|
||||
SelectCriminalReason msg)
|
||||
{
|
||||
var hasServer = new EventCheckServer();
|
||||
RaiseLocalEvent(hasServer);
|
||||
|
||||
if (!hasServer.Result)
|
||||
return;
|
||||
|
||||
var ev = new EventChangeReason(msg.SelectedKey, msg.Text);
|
||||
RaiseLocalEvent(ev);
|
||||
|
||||
UpdateUserInterface(uid, component);
|
||||
}
|
||||
|
||||
private void OnStatusSelected(EntityUid uid, CriminalRecordsConsoleComponent component,
|
||||
SelectCriminalStatus msg)
|
||||
{
|
||||
if (msg.SelectedStatus == null)
|
||||
return;
|
||||
|
||||
var hasServer = new EventCheckServer();
|
||||
RaiseLocalEvent(hasServer);
|
||||
|
||||
if (!hasServer.Result)
|
||||
return;
|
||||
|
||||
var messageId = "null";
|
||||
switch (msg.SelectedStatus.CriminalType)
|
||||
{
|
||||
case EnumCriminalRecordType.Released:
|
||||
messageId = "criminal-targetchannel-set-released";
|
||||
break;
|
||||
case EnumCriminalRecordType.Discharged:
|
||||
messageId = "criminal-targetchannel-set-discharged";
|
||||
break;
|
||||
case EnumCriminalRecordType.Parolled:
|
||||
messageId = "criminal-targetchannel-set-parolled";
|
||||
break;
|
||||
case EnumCriminalRecordType.Suspected:
|
||||
messageId = "criminal-targetchannel-set-suspected";
|
||||
break;
|
||||
case EnumCriminalRecordType.Wanted:
|
||||
messageId = "criminal-targetchannel-set-wanted";
|
||||
break;
|
||||
case EnumCriminalRecordType.Incarcerated:
|
||||
messageId = "criminal-targetchannel-set-incarcerated";
|
||||
break;
|
||||
}
|
||||
|
||||
var message = "";
|
||||
|
||||
if (msg.SelectedStatus.Reason != string.Empty)
|
||||
{
|
||||
messageId += "-reason";
|
||||
message = Loc.GetString(messageId,
|
||||
("target", msg.SelectedStatus.StationRecord.Name),
|
||||
("reason", msg.SelectedStatus.Reason));
|
||||
}
|
||||
else
|
||||
{
|
||||
message = Loc.GetString(messageId,
|
||||
("target", msg.SelectedStatus.StationRecord.Name));
|
||||
}
|
||||
|
||||
_radioSystem.SendRadioMessage(uid, message, _prototype.Index<RadioChannelPrototype>(component.TargetChannel), uid);
|
||||
|
||||
var ev = new EventChangeCache(msg.SelectedKey, msg.SelectedStatus);
|
||||
RaiseLocalEvent(ev);
|
||||
|
||||
UpdateUserInterface(uid, component);
|
||||
}
|
||||
|
||||
private void UpdateUserInterface(EntityUid uid,
|
||||
CriminalRecordsConsoleComponent? console = null)
|
||||
{
|
||||
if (!Resolve(uid, ref console))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Dirty(console);
|
||||
|
||||
var owningStation = _stationSystem.GetOwningStation(uid);
|
||||
|
||||
if (!TryComp<StationRecordsComponent>(owningStation, out var stationRecordsComponent))
|
||||
{
|
||||
CriminalRecordsConsoleBuiState state = new(null, null, null, null, null, false); //null
|
||||
SetStateForInterface(uid, state);
|
||||
return;
|
||||
}
|
||||
|
||||
var consoleRecords =
|
||||
_stationRecordsSystem.GetRecordsOfType<GeneralStationRecord>(owningStation.Value, stationRecordsComponent);
|
||||
|
||||
var listing = new Dictionary<StationRecordKey, string>();
|
||||
|
||||
foreach (var pair in consoleRecords)
|
||||
{
|
||||
listing.Add(pair.Item1, pair.Item2.Name);
|
||||
}
|
||||
|
||||
if (listing.Count == 0)
|
||||
{
|
||||
CriminalRecordsConsoleBuiState state = new(null, null, null, null, null, false); //console!.Filter
|
||||
SetStateForInterface(uid, state);
|
||||
return;
|
||||
}
|
||||
else if (listing.Count == 1)
|
||||
{
|
||||
console!.ActiveKey = listing.Keys.First();
|
||||
}
|
||||
|
||||
GeneralStationRecord? record = null;
|
||||
if (console!.ActiveKey != null)
|
||||
{
|
||||
_stationRecordsSystem.TryGetRecord(owningStation.Value, console.ActiveKey.Value, out record,
|
||||
stationRecordsComponent);
|
||||
}
|
||||
|
||||
var serverEv = new EventGetCache();
|
||||
RaiseLocalEvent(serverEv);
|
||||
|
||||
var idCardInfo = console.ContainedID != null ? new IdCardNetInfo(console.ContainedID.FullName, console.ContainedID.JobTitle) : null;
|
||||
|
||||
CriminalRecordsConsoleBuiState newState = new(console.ActiveKey, record, listing, serverEv.Cache, idCardInfo, AccessCheck(console.ContainedID)); //console.Filter
|
||||
SetStateForInterface(uid, newState);
|
||||
}
|
||||
|
||||
private void SetStateForInterface(EntityUid uid, CriminalRecordsConsoleBuiState newState)
|
||||
{
|
||||
var ui = _userInterface.GetUiOrNull(uid, CriminalRecordsConsoleKey.Key);
|
||||
if (ui != null)
|
||||
_userInterface.SetUiState(ui, newState);
|
||||
}
|
||||
|
||||
private bool AccessCheck(IdCardComponent? component)
|
||||
{
|
||||
if (component is null)
|
||||
return false;
|
||||
|
||||
var uid = component.Owner;
|
||||
|
||||
if (!EntityManager.TryGetComponent(uid, out AccessComponent? reader))
|
||||
return false;
|
||||
|
||||
foreach (var tag in reader.Tags)
|
||||
{
|
||||
var proto = _prototype.Index<EntityPrototype>("ComputerCriminalRecords");
|
||||
proto.TryGetComponent(out AccessReaderComponent? access);
|
||||
|
||||
if (access == null)
|
||||
continue;
|
||||
|
||||
if (access.AccessLists.SelectMany(list => list).Any(entry => entry == tag))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public sealed class AccessReaderSystem : EntitySystem
|
||||
if (!id.IsValid())
|
||||
continue;
|
||||
|
||||
component.AccessKeys.Add(new StationRecordKey(key.Item2, id));
|
||||
component.AccessKeys.Add(new StationRecordKey(key.Item2, GetNetEntity(id)));
|
||||
}
|
||||
|
||||
component.AccessLists = new(state.AccessLists);
|
||||
|
||||
@@ -14,11 +14,11 @@ public abstract class SharedStationRecordsSystem : EntitySystem
|
||||
|
||||
public StationRecordKey Convert((NetEntity, uint) input)
|
||||
{
|
||||
return new StationRecordKey(input.Item2, GetEntity(input.Item1));
|
||||
return new StationRecordKey(input.Item2, input.Item1);
|
||||
}
|
||||
public (NetEntity, uint) Convert(StationRecordKey input)
|
||||
{
|
||||
return (GetNetEntity(input.OriginStation), input.Id);
|
||||
return (input.OriginStation, input.Id);
|
||||
}
|
||||
|
||||
public List<(NetEntity, uint)> Convert(ICollection<StationRecordKey> input)
|
||||
|
||||
@@ -1,18 +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 : IEquatable<StationRecordKey>
|
||||
{
|
||||
[DataField("id")]
|
||||
public readonly uint Id;
|
||||
|
||||
[DataField("station")]
|
||||
public readonly EntityUid OriginStation;
|
||||
public readonly NetEntity OriginStation;
|
||||
|
||||
public static StationRecordKey Invalid = default;
|
||||
|
||||
public StationRecordKey(uint id, EntityUid originStation)
|
||||
public StationRecordKey(uint id, NetEntity originStation)
|
||||
{
|
||||
Id = id;
|
||||
OriginStation = originStation;
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared.White.CriminalRecords;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class CriminalRecordsConsoleComponent : Component
|
||||
{
|
||||
public const string IdSlotId = "id-slot";
|
||||
|
||||
[DataField("idSlot")]
|
||||
public ItemSlot IdSlot = new();
|
||||
|
||||
[DataField("TargetChannel", customTypeSerializer: typeof(PrototypeIdSerializer<RadioChannelPrototype>))]
|
||||
public string TargetChannel = "Security";
|
||||
|
||||
[ViewVariables] public IdCardComponent? ContainedID;
|
||||
[ViewVariables] public StationRecordKey? ActiveKey { get; set; }
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class IdCardNetInfo
|
||||
{
|
||||
public string? FullName { get; }
|
||||
public string? JobTitle { get; }
|
||||
|
||||
public IdCardNetInfo(string? fullName, string? jobTitle)
|
||||
{
|
||||
FullName = fullName;
|
||||
JobTitle = jobTitle;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class CriminalRecordsConsoleBuiState : BoundUserInterfaceState
|
||||
{
|
||||
/// <summary>
|
||||
/// Current selected key.
|
||||
/// </summary>
|
||||
public StationRecordKey? SelectedKey { get; }
|
||||
public GeneralStationRecord? Record { get; }
|
||||
public Dictionary<StationRecordKey, string>? RecordListing { get; }
|
||||
public Dictionary<StationRecordKey, CriminalRecordInfo>? Cache { get; }
|
||||
public IdCardNetInfo? ContainedId { get; }
|
||||
public bool IsAllowed { get; }
|
||||
//public GeneralStationRecordsFilter? Filter { get; }
|
||||
public CriminalRecordsConsoleBuiState(StationRecordKey? key, GeneralStationRecord? record,
|
||||
Dictionary<StationRecordKey, string>? recordListing, Dictionary<StationRecordKey, CriminalRecordInfo>? cache
|
||||
, IdCardNetInfo? containedId, bool isAllowed) //GeneralStationRecordsFilter? newFilter
|
||||
{
|
||||
SelectedKey = key;
|
||||
Record = record;
|
||||
RecordListing = recordListing;
|
||||
Cache = cache;
|
||||
ContainedId = containedId;
|
||||
IsAllowed = isAllowed;
|
||||
//Filter = newFilter;
|
||||
}
|
||||
|
||||
public bool IsEmpty() => SelectedKey == null
|
||||
&& Record == null && RecordListing == null;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class SelectCriminalRecord : BoundUserInterfaceMessage
|
||||
{
|
||||
public StationRecordKey? SelectedKey { get; }
|
||||
//
|
||||
public SelectCriminalRecord(StationRecordKey? selectedKey)
|
||||
{
|
||||
SelectedKey = selectedKey;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class SelectCriminalStatus : BoundUserInterfaceMessage
|
||||
{
|
||||
public StationRecordKey SelectedKey { get; }
|
||||
public CriminalRecordInfo? SelectedStatus { get; }
|
||||
|
||||
public SelectCriminalStatus(StationRecordKey selectedKey, CriminalRecordInfo? selectedStatus)
|
||||
{
|
||||
SelectedKey = selectedKey;
|
||||
SelectedStatus = selectedStatus;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class SelectCriminalReason : BoundUserInterfaceMessage
|
||||
{
|
||||
public StationRecordKey SelectedKey { get; }
|
||||
public string Text { get; }
|
||||
|
||||
public SelectCriminalReason(StationRecordKey key, string text)
|
||||
{
|
||||
SelectedKey = key;
|
||||
Text = text;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum CriminalRecordsConsoleKey : byte
|
||||
{
|
||||
Key
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.White.CriminalRecords;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum EnumCriminalRecordType
|
||||
{
|
||||
Released = 0,
|
||||
Discharged = 1,
|
||||
Parolled = 2,
|
||||
Suspected = 3,
|
||||
Wanted = 4,
|
||||
Incarcerated = 5
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class CriminalRecordInfo
|
||||
{
|
||||
|
||||
// Main data
|
||||
[DataField("StationRecord")] public GeneralStationRecord StationRecord { get; set; }
|
||||
|
||||
[DataField("CriminalType")] public EnumCriminalRecordType CriminalType { get; set; }
|
||||
[DataField("Reason")] public string Reason { get; set; }
|
||||
|
||||
public CriminalRecordInfo(GeneralStationRecord stationRecord, EnumCriminalRecordType criminalType, string reason)
|
||||
{
|
||||
this.StationRecord = stationRecord;
|
||||
this.CriminalType = criminalType;
|
||||
this.Reason = reason;
|
||||
}
|
||||
}
|
||||
|
||||
[RegisterComponent]
|
||||
[NetworkedComponent]
|
||||
public sealed partial class CriminalRecordsServerComponent : Component
|
||||
{
|
||||
[DataField("Cache")] public Dictionary<StationRecordKey, CriminalRecordInfo> Cache = new();
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class CriminalRecordsServerComponentState : ComponentState
|
||||
{
|
||||
public Dictionary<StationRecordKey, CriminalRecordInfo> Cache { get; init; }
|
||||
|
||||
public CriminalRecordsServerComponentState(Dictionary<StationRecordKey, CriminalRecordInfo> cache)
|
||||
{
|
||||
Cache = cache;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.White.CriminalRecords;
|
||||
|
||||
public sealed class CriminalRecordsServerSystem: EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<EventGetCache>(OnGetCache);
|
||||
SubscribeLocalEvent<EventChangeCache>(OnChangeCache);
|
||||
SubscribeLocalEvent<EventChangeReason>(OnChangeReason);
|
||||
SubscribeLocalEvent<EventCheckServer>(OnCheckServer);
|
||||
SubscribeLocalEvent<CriminalRecordsServerComponent, ComponentGetState>(OnGetState);
|
||||
SubscribeLocalEvent<CriminalRecordsServerComponent, ComponentHandleState>(OnHandleState);
|
||||
}
|
||||
|
||||
private void OnGetState(EntityUid uid, CriminalRecordsServerComponent component, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new CriminalRecordsServerComponent.CriminalRecordsServerComponentState(component.Cache);
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid uid, CriminalRecordsServerComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not CriminalRecordsServerComponent.CriminalRecordsServerComponentState state)
|
||||
return;
|
||||
|
||||
component.Cache = state.Cache;
|
||||
}
|
||||
|
||||
private void OnGetCache(EventGetCache ev)
|
||||
{
|
||||
var serverList = EntityQuery<CriminalRecordsServerComponent>();
|
||||
foreach (var server in serverList)
|
||||
{
|
||||
ev.Cache = server.Cache;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnChangeCache(EventChangeCache ev)
|
||||
{
|
||||
var serverList = EntityQuery<CriminalRecordsServerComponent>();
|
||||
foreach (var server in serverList)
|
||||
{
|
||||
foreach (var (key, info) in server.Cache)
|
||||
{
|
||||
if (key.Id == ev.Key.Id)
|
||||
{
|
||||
info.Reason = ev.Record.Reason;
|
||||
info.CriminalType = ev.Record.CriminalType;
|
||||
Dirty(server);
|
||||
return;
|
||||
}
|
||||
}
|
||||
server.Cache.Add(ev.Key, ev.Record);
|
||||
Dirty(server);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnChangeReason(EventChangeReason ev)
|
||||
{
|
||||
var serverList = EntityQuery<CriminalRecordsServerComponent>();
|
||||
foreach (var server in serverList)
|
||||
{
|
||||
foreach (var (key, info) in server.Cache)
|
||||
{
|
||||
if (key.Id == ev.Key.Id)
|
||||
{
|
||||
info.Reason = ev.Text;
|
||||
Dirty(server);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCheckServer(EventCheckServer ev)
|
||||
{
|
||||
var serverList = EntityQuery<CriminalRecordsServerComponent>();
|
||||
foreach (var server in serverList)
|
||||
{
|
||||
ev.Result = true;
|
||||
return;
|
||||
}
|
||||
ev.Result = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Events
|
||||
public sealed class EventGetCache
|
||||
{
|
||||
public Dictionary<StationRecordKey, CriminalRecordInfo> Cache = new();
|
||||
|
||||
public EventGetCache()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EventChangeCache
|
||||
{
|
||||
public CriminalRecordInfo Record { get; }
|
||||
public StationRecordKey Key { get; }
|
||||
|
||||
public EventChangeCache(StationRecordKey key, CriminalRecordInfo record)
|
||||
{
|
||||
Key = key;
|
||||
Record = record;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EventChangeReason
|
||||
{
|
||||
public string Text { get; }
|
||||
public StationRecordKey Key { get; }
|
||||
|
||||
public EventChangeReason(StationRecordKey key, string text)
|
||||
{
|
||||
Key = key;
|
||||
Text = text;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EventCheckServer
|
||||
{
|
||||
public bool Result { get; set; }
|
||||
|
||||
public EventCheckServer()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Content.Shared.White.EntityCrimeRecords
|
||||
{
|
||||
/// <summary>
|
||||
/// Allow to see crime records info for character
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class ShowCrimeRecordsComponent : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
39
Resources/Locale/ru-RU/white/criminal-console.ftl
Normal file
@@ -0,0 +1,39 @@
|
||||
criminal-console-list = Список экипажа
|
||||
criminal-console-name = консоль криминальных записей
|
||||
criminal-grant-status-button-name = Статус
|
||||
|
||||
criminal-login-out = Log Out
|
||||
criminal-login-in = Log In
|
||||
criminal-login-info = ID: {$user}
|
||||
criminal-login-hint = Вставьте ID карту в консоль, нажав на "{$name}" (нужно держать ID карту в руке)
|
||||
criminal-login-warn = WARNING: Доступ к системе осуществляется уровнем доступа "службы безопасности"
|
||||
|
||||
criminal-status-released = Освобожден
|
||||
criminal-status-discharged = Выписан
|
||||
criminal-status-parolled = Закодирован
|
||||
criminal-status-suspected = Подозреваемый
|
||||
criminal-status-wanted = В розыске
|
||||
criminal-status-incarcerated = Заключенный
|
||||
|
||||
criminal-targetchannel-set-released = {$target} освобожден(а).
|
||||
criminal-targetchannel-set-released-reason = {$target} освобожден(а). Заметка: {$reason}.
|
||||
criminal-targetchannel-set-discharged = {$target} выписан(а).
|
||||
criminal-targetchannel-set-discharged-reason = {$target} выписан(а). Заметка: {$reason}.
|
||||
criminal-targetchannel-set-parolled = {$target} закодирован(а).
|
||||
criminal-targetchannel-set-parolled-reason = {$target} закодирован(а). Заметка: {$reason}.
|
||||
criminal-targetchannel-set-suspected = {$target} под подозрением.
|
||||
criminal-targetchannel-set-suspected-reason = {$target} под подозрением. Заметка: {$reason}.
|
||||
criminal-targetchannel-set-wanted = {$target} объявлен(а) в розыск.
|
||||
criminal-targetchannel-set-wanted-reason = {$target} объявлен(а) в розыск. Заметка: {$reason}.
|
||||
criminal-targetchannel-set-incarcerated = {$target} был(а) заключен(а) под стражу.
|
||||
criminal-targetchannel-set-incarcerated-reason = {$target} был(а) заключен(а) под стражу. Заметка: {$reason}.
|
||||
|
||||
criminal-dna-name = ДНК:
|
||||
criminal-dna-desc = - [color={$color}]{$info}[/color]
|
||||
criminal-fingerprint-name = Отпечатки пальцев:
|
||||
criminal-fingerprint-desc = - [color={$color}]{$info}[/color]
|
||||
|
||||
criminal-detail-info = Заметка:
|
||||
|
||||
ent-CriminalRecordsServer = сервер криминальных записей
|
||||
.desc = Содержит все преступные записи об экипажа на станции. Не дайте злоумышлиникам уничтожить его!
|
||||
@@ -281,12 +281,23 @@
|
||||
- type: Computer
|
||||
board: MedicalRecordsComputerCircuitboard
|
||||
|
||||
# mark this is needed
|
||||
- type: entity
|
||||
parent: BaseComputer
|
||||
id: ComputerCriminalRecords
|
||||
name: criminal records computer
|
||||
description: This can be used to check criminal records.
|
||||
components:
|
||||
- type: CriminalRecordsConsole # my own impl
|
||||
idSlot:
|
||||
name: ID Card
|
||||
ejectSound: /Audio/Machines/id_swipe.ogg
|
||||
insertSound: /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg
|
||||
ejectOnBreak: true
|
||||
swap: false
|
||||
whitelist:
|
||||
components:
|
||||
- IdCard
|
||||
- type: Sprite
|
||||
layers:
|
||||
- map: ["computerLayerBody"]
|
||||
@@ -297,12 +308,28 @@
|
||||
state: explosive
|
||||
- map: ["computerLayerKeys"]
|
||||
state: security_key
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
- key: enum.CriminalRecordsConsoleKey.Key
|
||||
type: CriminalRecordsBoundUserInterface
|
||||
- type: ActivatableUI
|
||||
key: enum.CriminalRecordsConsoleKey.Key
|
||||
- type: PointLight
|
||||
radius: 1.5
|
||||
energy: 1.6
|
||||
color: "#1f8c28"
|
||||
- type: Computer
|
||||
board: CriminalRecordsComputerCircuitboard
|
||||
- type: Tag
|
||||
tags:
|
||||
- EmagImmune
|
||||
- type: ItemSlots
|
||||
- type: ContainerContainer
|
||||
containers:
|
||||
board: !type:Container
|
||||
id-slot: !type:ContainerSlot
|
||||
- type: AccessReader
|
||||
access: [["Security"]]
|
||||
|
||||
- type: entity
|
||||
parent: BaseComputer
|
||||
|
||||
@@ -781,13 +781,3 @@
|
||||
Steel: 100
|
||||
Glass: 900
|
||||
Gold: 100
|
||||
|
||||
- type: latheRecipe
|
||||
id: ClothingEyesHudMedical
|
||||
icon: { sprite: Clothing/Eyes/Hud/med.rsi, state: icon }
|
||||
result: ClothingEyesHudMedical
|
||||
completetime: 4
|
||||
materials:
|
||||
Steel: 100
|
||||
Glass: 300
|
||||
Plasma: 200
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
- type: entity
|
||||
parent: [ BaseMachinePowered, ConstructibleMachine ]
|
||||
id: CriminalRecordsServer
|
||||
name: criminal records server
|
||||
description: When powered and filled with encryption keys it allows radio headset communication.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: White/Structures/Machines/criminal_record_server.rsi
|
||||
snapCardinals: true
|
||||
netsync: false
|
||||
layers:
|
||||
- state: icon
|
||||
- state: unlit
|
||||
shader: unshaded
|
||||
map: ["enum.PowerDeviceVisualLayers.Powered"]
|
||||
- state: panel
|
||||
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.PowerDeviceVisuals.Powered:
|
||||
enum.PowerDeviceVisualLayers.Powered:
|
||||
True: { visible: true }
|
||||
False: { visible: false }
|
||||
- type: Appearance
|
||||
- type: Physics
|
||||
bodyType: Static
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
fix1:
|
||||
shape:
|
||||
!type:PhysShapeAabb
|
||||
bounds: "-0.4,-0.4,0.4,0.4"
|
||||
density: 190
|
||||
mask:
|
||||
- MachineMask
|
||||
layer:
|
||||
- MachineLayer
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
damage: 100
|
||||
behaviors:
|
||||
- !type:ChangeConstructionNodeBehavior
|
||||
node: machineFrame
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
- type: Machine
|
||||
board: CriminalRecordsServerCircuitboard
|
||||
- type: WiresPanel
|
||||
- type: Wires
|
||||
boardName: "CriminalRecordsServer"
|
||||
layoutId: CriminalRecordsServer
|
||||
- type: Transform
|
||||
anchored: true
|
||||
- type: Pullable
|
||||
- type: CriminalRecordsServer
|
||||
- type: ContainerContainer
|
||||
containers:
|
||||
machine_board: !type:Container
|
||||
machine_parts: !type:Container
|
||||
|
||||
- type: entity
|
||||
id: CriminalRecordsServerCircuitboard
|
||||
parent: BaseMachineCircuitboard
|
||||
name: criminal records server machine board
|
||||
description: A machine printed circuit board for an telecommunication server.
|
||||
components:
|
||||
- type: MachineBoard
|
||||
prototype: CriminalRecordsServer
|
||||
materialRequirements:
|
||||
Steel: 1
|
||||
Cable: 2
|
||||
@@ -0,0 +1,47 @@
|
||||
- type: statusIcon
|
||||
id: CriminalRecordIcon
|
||||
abstract: true
|
||||
priority: 3
|
||||
locationPreference: Right
|
||||
|
||||
- type: statusIcon
|
||||
parent: CriminalRecordIcon
|
||||
id: CriminalRecordIconReleased
|
||||
icon:
|
||||
sprite: White/Interface/records.rsi
|
||||
state: released
|
||||
|
||||
- type: statusIcon
|
||||
parent: CriminalRecordIcon
|
||||
id: CriminalRecordIconDischarged
|
||||
icon:
|
||||
sprite: White/Interface/records.rsi
|
||||
state: discharged
|
||||
|
||||
- type: statusIcon
|
||||
parent: CriminalRecordIcon
|
||||
id: CriminalRecordIconParolled
|
||||
icon:
|
||||
sprite: White/Interface/records.rsi
|
||||
state: parolled
|
||||
|
||||
- type: statusIcon
|
||||
parent: CriminalRecordIcon
|
||||
id: CriminalRecordIconSuspected
|
||||
icon:
|
||||
sprite: White/Interface/records.rsi
|
||||
state: suspected
|
||||
|
||||
- type: statusIcon
|
||||
parent: CriminalRecordIcon
|
||||
id: CriminalRecordIconWanted
|
||||
icon:
|
||||
sprite: White/Interface/records.rsi
|
||||
state: wanted
|
||||
|
||||
- type: statusIcon
|
||||
parent: CriminalRecordIcon
|
||||
id: CriminalRecordIconIncarcerated
|
||||
icon:
|
||||
sprite: White/Interface/records.rsi
|
||||
state: incarcerated
|
||||
BIN
Resources/Textures/White/Interface/records.rsi/discharged.png
Normal file
|
After Width: | Height: | Size: 144 B |
BIN
Resources/Textures/White/Interface/records.rsi/incarcerated.png
Normal file
|
After Width: | Height: | Size: 139 B |
27
Resources/Textures/White/Interface/records.rsi/meta.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "https://github.com/tgstation/tgstation/blob/8e49222b72f6fdcbe741d2a6ce0a8425d95010b7/icons/mob/huds/hud.dmi",
|
||||
"version": 1,
|
||||
"size": { "y": 8, "x": 8 },
|
||||
"states": [
|
||||
{
|
||||
"name": "wanted"
|
||||
},
|
||||
{
|
||||
"name": "suspected"
|
||||
},
|
||||
{
|
||||
"name": "released"
|
||||
},
|
||||
{
|
||||
"name": "parolled"
|
||||
},
|
||||
{
|
||||
"name": "incarcerated"
|
||||
},
|
||||
{
|
||||
"name": "discharged"
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/White/Interface/records.rsi/parolled.png
Normal file
|
After Width: | Height: | Size: 150 B |
BIN
Resources/Textures/White/Interface/records.rsi/released.png
Normal file
|
After Width: | Height: | Size: 143 B |
BIN
Resources/Textures/White/Interface/records.rsi/suspected.png
Normal file
|
After Width: | Height: | Size: 141 B |
BIN
Resources/Textures/White/Interface/records.rsi/wanted.png
Normal file
|
After Width: | Height: | Size: 147 B |
|
After Width: | Height: | Size: 416 B |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/blob/9c3494fd79e6bf8dc532300b9de4f688ff276ac9/icons/obj/machines/telecomms.dmi",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "unlit",
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "icon"
|
||||
},
|
||||
{
|
||||
"name": "panel"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 860 B |
|
After Width: | Height: | Size: 1.8 KiB |