Wanted list cartridge (#31223)

* WantedListCartridge has been added

* WantedListCartridge user interface works

* WantedListCartridge is added as standard in some PDAs

* The CriminalRecordsSystem can now also take into account who created the record

* Added offense history table

* Fix of missing loaderUid for a cartridge without installing the program

* Added personalized information about the target

* The crime history has been finalized

* Added StatusList

* The officer's name has been added to the automatic history

* WantedListCartridge has been added to the HOS locker

* WantedListCartridge has been removed from brigmedic's preset programs

* The StealConditionSystem now takes into account whether a cartridge is inserted or installed

* Added target to thief on WantedListCartridge

* Merge fix

* Removing copypaste

* Fix merge 2

* The sprite of WantedListCartridge has been changed

* Update pda.yml

* Fix scrollbar in the history table

* Upstream localization fix

* `StatusList` has been replaced by `ListContainer` with `TextureRect`

* Margin fix
This commit is contained in:
Эдуард
2024-09-19 13:22:02 +03:00
committed by GitHub
parent 75ff65d108
commit 1468cbdb8a
22 changed files with 579 additions and 34 deletions

View File

@@ -0,0 +1,30 @@
using Content.Client.UserInterface.Fragments;
using Content.Shared.CartridgeLoader.Cartridges;
using Robust.Client.UserInterface;
namespace Content.Client.CartridgeLoader.Cartridges;
public sealed partial class WantedListUi : UIFragment
{
private WantedListUiFragment? _fragment;
public override Control GetUIFragmentRoot()
{
return _fragment!;
}
public override void Setup(BoundUserInterface userInterface, EntityUid? fragmentOwner)
{
_fragment = new WantedListUiFragment();
}
public override void UpdateState(BoundUserInterfaceState state)
{
switch (state)
{
case WantedListUiState cast:
_fragment?.UpdateState(cast.Records);
break;
}
}
}

View File

@@ -0,0 +1,240 @@
using System.Linq;
using Content.Client.UserInterface.Controls;
using Content.Shared.CriminalRecords.Systems;
using Content.Shared.Security;
using Content.Shared.StatusIcon;
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.XAML;
using Robust.Shared.Input;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.CartridgeLoader.Cartridges;
[GenerateTypedNameReferences]
public sealed partial class WantedListUiFragment : BoxContainer
{
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private readonly SpriteSystem _spriteSystem;
private string? _selectedTargetName;
private List<WantedRecord> _wantedRecords = new();
public WantedListUiFragment()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_spriteSystem = _entitySystem.GetEntitySystem<SpriteSystem>();
SearchBar.OnTextChanged += OnSearchBarTextChanged;
}
private void OnSearchBarTextChanged(LineEdit.LineEditEventArgs args)
{
var found = !String.IsNullOrWhiteSpace(args.Text)
? _wantedRecords.FindAll(r =>
r.TargetInfo.Name.Contains(args.Text) ||
r.Status.ToString().Contains(args.Text, StringComparison.OrdinalIgnoreCase))
: _wantedRecords;
UpdateState(found, false);
}
public void UpdateState(List<WantedRecord> records, bool refresh = true)
{
if (records.Count == 0)
{
NoRecords.Visible = true;
RecordsList.Visible = false;
RecordUnselected.Visible = false;
PersonContainer.Visible = false;
_selectedTargetName = null;
if (refresh)
_wantedRecords.Clear();
RecordsList.PopulateList(new List<ListData>());
return;
}
NoRecords.Visible = false;
RecordsList.Visible = true;
RecordUnselected.Visible = true;
PersonContainer.Visible = false;
var dataList = records.Select(r => new StatusListData(r)).ToList();
RecordsList.GenerateItem = GenerateItem;
RecordsList.ItemPressed = OnItemSelected;
RecordsList.PopulateList(dataList);
if (refresh)
_wantedRecords = records;
}
private void OnItemSelected(BaseButton.ButtonEventArgs args, ListData data)
{
if (data is not StatusListData(var record))
return;
FormattedMessage GetLoc(string fluentId, params (string,object)[] args)
{
var msg = new FormattedMessage();
var fluent = Loc.GetString(fluentId, args);
msg.AddMarkupPermissive(fluent);
return msg;
}
// Set personal info
PersonName.Text = record.TargetInfo.Name;
TargetAge.SetMessage(GetLoc(
"wanted-list-age-label",
("age", record.TargetInfo.Age)
));
TargetJob.SetMessage(GetLoc(
"wanted-list-job-label",
("job", record.TargetInfo.JobTitle.ToLower())
));
TargetSpecies.SetMessage(GetLoc(
"wanted-list-species-label",
("species", record.TargetInfo.Species.ToLower())
));
TargetGender.SetMessage(GetLoc(
"wanted-list-gender-label",
("gender", record.TargetInfo.Gender)
));
// Set reason
WantedReason.SetMessage(GetLoc(
"wanted-list-reason-label",
("reason", record.Reason ?? Loc.GetString("wanted-list-unknown-reason-label"))
));
// Set status
PersonState.SetMessage(GetLoc(
"wanted-list-status-label",
("status", record.Status.ToString().ToLower())
));
// Set initiator
InitiatorName.SetMessage(GetLoc(
"wanted-list-initiator-label",
("initiator", record.Initiator ?? Loc.GetString("wanted-list-unknown-initiator-label"))
));
// History table
// Clear table if it exists
HistoryTable.RemoveAllChildren();
HistoryTable.AddChild(new Label()
{
Text = Loc.GetString("wanted-list-history-table-time-col"),
StyleClasses = { "LabelSmall" },
HorizontalAlignment = HAlignment.Center,
});
HistoryTable.AddChild(new Label()
{
Text = Loc.GetString("wanted-list-history-table-reason-col"),
StyleClasses = { "LabelSmall" },
HorizontalAlignment = HAlignment.Center,
HorizontalExpand = true,
});
HistoryTable.AddChild(new Label()
{
Text = Loc.GetString("wanted-list-history-table-initiator-col"),
StyleClasses = { "LabelSmall" },
HorizontalAlignment = HAlignment.Center,
});
if (record.History.Count > 0)
{
HistoryTable.Visible = true;
foreach (var history in record.History.OrderByDescending(h => h.AddTime))
{
HistoryTable.AddChild(new Label()
{
Text = $"{history.AddTime.Hours:00}:{history.AddTime.Minutes:00}:{history.AddTime.Seconds:00}",
StyleClasses = { "LabelSmall" },
VerticalAlignment = VAlignment.Top,
});
HistoryTable.AddChild(new RichTextLabel()
{
Text = $"[color=white]{history.Crime}[/color]",
HorizontalExpand = true,
VerticalAlignment = VAlignment.Top,
StyleClasses = { "LabelSubText" },
Margin = new(10f, 0f),
});
HistoryTable.AddChild(new RichTextLabel()
{
Text = $"[color=white]{history.InitiatorName}[/color]",
StyleClasses = { "LabelSubText" },
VerticalAlignment = VAlignment.Top,
});
}
}
RecordUnselected.Visible = false;
PersonContainer.Visible = true;
// Save selected item
_selectedTargetName = record.TargetInfo.Name;
}
private void GenerateItem(ListData data, ListContainerButton button)
{
if (data is not StatusListData(var record))
return;
var box = new BoxContainer() { Orientation = LayoutOrientation.Horizontal, HorizontalExpand = true };
var label = new Label() { Text = record.TargetInfo.Name };
var rect = new TextureRect()
{
TextureScale = new(2.2f),
VerticalAlignment = VAlignment.Center,
HorizontalAlignment = HAlignment.Center,
Margin = new(0f, 0f, 6f, 0f),
};
if (record.Status is not SecurityStatus.None)
{
var proto = "SecurityIcon" + record.Status switch
{
SecurityStatus.Detained => "Incarcerated",
_ => record.Status.ToString(),
};
if (_prototypeManager.TryIndex<SecurityIconPrototype>(proto, out var prototype))
{
rect.Texture = _spriteSystem.Frame0(prototype.Icon);
}
}
box.AddChild(rect);
box.AddChild(label);
button.AddChild(box);
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
if (record.TargetInfo.Name.Equals(_selectedTargetName))
{
button.Pressed = true;
// For some reason the event is not called when `Pressed` changed, call it manually.
OnItemSelected(
new(button, new(new(), BoundKeyState.Down, new(), false, new(), new())),
data);
}
}
}
internal record StatusListData(WantedRecord Record) : ListData;

View File

@@ -0,0 +1,50 @@
<cartridges:WantedListUiFragment xmlns:cartridges="clr-namespace:Content.Client.CartridgeLoader.Cartridges"
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Orientation="Vertical"
VerticalExpand="True"
HorizontalExpand="True">
<LineEdit Name="SearchBar" PlaceHolder="{Loc 'wanted-list-search-placeholder'}"/>
<BoxContainer Name="MainContainer" Orientation="Horizontal" HorizontalExpand="True" VerticalExpand="True">
<Label Name="NoRecords" Text="{Loc 'wanted-list-label-no-records'}" Align="Center" VAlign="Center" HorizontalExpand="True" FontColorOverride="DarkGray"/>
<!-- Any attempts to set dimensions for ListContainer breaks the renderer, I have to roughly set sizes and margins in other controllers. -->
<controls:ListContainer
Name="RecordsList"
HorizontalAlignment="Left"
VerticalExpand="True"
Visible="False"
Toggle="True"
Group="True"
SetWidth="192" />
<Label Name="RecordUnselected"
Text="{Loc 'criminal-records-console-select-record-info'}"
Align="Center"
FontColorOverride="DarkGray"
Visible="False"
HorizontalExpand="True" />
<BoxContainer Name="PersonContainer" Orientation="Vertical" HorizontalExpand="True" SetWidth="334" Margin="5 0 77 0">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Name="PersonName" StyleClasses="LabelBig" />
<RichTextLabel Name="PersonState" HorizontalAlignment="Right" HorizontalExpand="True" />
</BoxContainer>
<PanelContainer StyleClasses="LowDivider" Margin="0 5 0 5"/>
<ScrollContainer VerticalExpand="True" HScrollEnabled="False">
<BoxContainer Name="DataContainer" Orientation="Vertical">
<RichTextLabel Name="TargetAge" />
<RichTextLabel Name="TargetJob" />
<RichTextLabel Name="TargetSpecies" />
<RichTextLabel Name="TargetGender" />
<PanelContainer StyleClasses="LowDivider" Margin="0 5 0 5"/>
<RichTextLabel Name="InitiatorName" VerticalAlignment="Stretch"/>
<RichTextLabel Name="WantedReason" VerticalAlignment="Stretch"/>
<PanelContainer StyleClasses="LowDivider" Margin="0 5 0 5" />
<controls:TableContainer Name="HistoryTable" Columns="3" Visible="False" HorizontalAlignment="Stretch" />
</BoxContainer>
</ScrollContainer>
</BoxContainer>
</BoxContainer>
</cartridges:WantedListUiFragment>

View File

@@ -340,6 +340,9 @@ public sealed class CartridgeLoaderSystem : SharedCartridgeLoaderSystem
if (args.Container.ID != InstalledContainerId && args.Container.ID != loader.CartridgeSlot.ID)
return;
if (TryComp(args.Entity, out CartridgeComponent? cartridge))
cartridge.LoaderUid = uid;
RaiseLocalEvent(args.Entity, new CartridgeAddedEvent(uid));
base.OnItemInserted(uid, loader, args);
}
@@ -360,6 +363,9 @@ public sealed class CartridgeLoaderSystem : SharedCartridgeLoaderSystem
if (deactivate)
RaiseLocalEvent(args.Entity, new CartridgeDeactivatedEvent(uid));
if (TryComp(args.Entity, out CartridgeComponent? cartridge))
cartridge.LoaderUid = null;
RaiseLocalEvent(args.Entity, new CartridgeRemovedEvent(uid));
base.OnItemRemoved(uid, loader, args);

View File

@@ -0,0 +1,8 @@
using Content.Shared.Security;
namespace Content.Server.CartridgeLoader.Cartridges;
[RegisterComponent]
public sealed partial class WantedListCartridgeComponent : Component
{
}

View File

@@ -68,6 +68,13 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
}
}
private void GetOfficer(EntityUid uid, out string officer)
{
var tryGetIdentityShortInfoEvent = new TryGetIdentityShortInfoEvent(null, uid);
RaiseLocalEvent(tryGetIdentityShortInfoEvent);
officer = tryGetIdentityShortInfoEvent.Title ?? Loc.GetString("criminal-records-console-unknown-officer");
}
private void OnChangeStatus(Entity<CriminalRecordsConsoleComponent> ent, ref CriminalRecordChangeStatus msg)
{
// prevent malf client violating wanted/reason nullability
@@ -90,29 +97,22 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
return;
}
var oldStatus = record.Status;
var name = _records.RecordName(key.Value);
GetOfficer(mob.Value, out var officer);
// when arresting someone add it to history automatically
// fallback exists if the player was not set to wanted beforehand
if (msg.Status == SecurityStatus.Detained)
{
var oldReason = record.Reason ?? Loc.GetString("criminal-records-console-unspecified-reason");
var history = Loc.GetString("criminal-records-console-auto-history", ("reason", oldReason));
_criminalRecords.TryAddHistory(key.Value, history);
_criminalRecords.TryAddHistory(key.Value, history, officer);
}
var oldStatus = record.Status;
// will probably never fail given the checks above
_criminalRecords.TryChangeStatus(key.Value, msg.Status, msg.Reason);
var name = _records.RecordName(key.Value);
var officer = Loc.GetString("criminal-records-console-unknown-officer");
var tryGetIdentityShortInfoEvent = new TryGetIdentityShortInfoEvent(null, mob.Value);
RaiseLocalEvent(tryGetIdentityShortInfoEvent);
if (tryGetIdentityShortInfoEvent.Title != null)
{
officer = tryGetIdentityShortInfoEvent.Title;
}
_criminalRecords.TryChangeStatus(key.Value, msg.Status, msg.Reason, officer);
(string, object)[] args;
if (reason != null)
@@ -152,14 +152,16 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
private void OnAddHistory(Entity<CriminalRecordsConsoleComponent> ent, ref CriminalRecordAddHistory msg)
{
if (!CheckSelected(ent, msg.Actor, out _, out var key))
if (!CheckSelected(ent, msg.Actor, out var mob, out var key))
return;
var line = msg.Line.Trim();
if (line.Length < 1 || line.Length > ent.Comp.MaxStringLength)
return;
if (!_criminalRecords.TryAddHistory(key.Value, line))
GetOfficer(mob.Value, out var officer);
if (!_criminalRecords.TryAddHistory(key.Value, line, officer))
return;
// no radio message since its not crucial to officers patrolling

View File

@@ -1,10 +1,15 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.CartridgeLoader;
using Content.Server.CartridgeLoader.Cartridges;
using Content.Server.StationRecords.Systems;
using Content.Shared.CriminalRecords;
using Content.Shared.CriminalRecords.Systems;
using Content.Shared.Security;
using Content.Shared.StationRecords;
using Content.Server.GameTicking;
using Content.Server.Station.Systems;
using Content.Shared.CartridgeLoader;
using Content.Shared.CartridgeLoader.Cartridges;
namespace Content.Server.CriminalRecords.Systems;
@@ -20,12 +25,18 @@ public sealed class CriminalRecordsSystem : SharedCriminalRecordsSystem
{
[Dependency] private readonly GameTicker _ticker = default!;
[Dependency] private readonly StationRecordsSystem _records = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly CartridgeLoaderSystem _cartridge = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AfterGeneralRecordCreatedEvent>(OnGeneralRecordCreated);
SubscribeLocalEvent<WantedListCartridgeComponent, CriminalRecordChangedEvent>(OnRecordChanged);
SubscribeLocalEvent<WantedListCartridgeComponent, CartridgeUiReadyEvent>(OnCartridgeUiReady);
SubscribeLocalEvent<WantedListCartridgeComponent, CriminalHistoryAddedEvent>(OnHistoryAdded);
SubscribeLocalEvent<WantedListCartridgeComponent, CriminalHistoryRemovedEvent>(OnHistoryRemoved);
}
private void OnGeneralRecordCreated(AfterGeneralRecordCreatedEvent ev)
@@ -39,14 +50,14 @@ public sealed class CriminalRecordsSystem : SharedCriminalRecordsSystem
/// Reason should only be passed if status is Wanted, nullability isn't checked.
/// </summary>
/// <returns>True if the status is changed, false if not</returns>
public bool TryChangeStatus(StationRecordKey key, SecurityStatus status, string? reason)
public bool TryChangeStatus(StationRecordKey key, SecurityStatus status, string? reason, string? initiatorName = null)
{
// don't do anything if its the same status
if (!_records.TryGetRecord<CriminalRecord>(key, out var record)
|| status == record.Status)
return false;
OverwriteStatus(key, record, status, reason);
OverwriteStatus(key, record, status, reason, initiatorName);
return true;
}
@@ -54,16 +65,24 @@ public sealed class CriminalRecordsSystem : SharedCriminalRecordsSystem
/// <summary>
/// Sets the status without checking previous status or reason nullability.
/// </summary>
public void OverwriteStatus(StationRecordKey key, CriminalRecord record, SecurityStatus status, string? reason)
public void OverwriteStatus(StationRecordKey key, CriminalRecord record, SecurityStatus status, string? reason, string? initiatorName = null)
{
record.Status = status;
record.Reason = reason;
record.InitiatorName = initiatorName;
var name = _records.RecordName(key);
if (name != string.Empty)
UpdateCriminalIdentity(name, status);
_records.Synchronize(key);
var args = new CriminalRecordChangedEvent(record);
var query = EntityQueryEnumerator<WantedListCartridgeComponent>();
while (query.MoveNext(out var readerUid, out _))
{
RaiseLocalEvent(readerUid, ref args);
}
}
/// <summary>
@@ -76,15 +95,23 @@ public sealed class CriminalRecordsSystem : SharedCriminalRecordsSystem
return false;
record.History.Add(entry);
var args = new CriminalHistoryAddedEvent(entry);
var query = EntityQueryEnumerator<WantedListCartridgeComponent>();
while (query.MoveNext(out var readerUid, out _))
{
RaiseLocalEvent(readerUid, ref args);
}
return true;
}
/// <summary>
/// Creates and tries to add a history entry using the current time.
/// </summary>
public bool TryAddHistory(StationRecordKey key, string line)
public bool TryAddHistory(StationRecordKey key, string line, string? initiatorName = null)
{
var entry = new CrimeHistory(_ticker.RoundDuration(), line);
var entry = new CrimeHistory(_ticker.RoundDuration(), line, initiatorName);
return TryAddHistory(key, entry);
}
@@ -100,7 +127,58 @@ public sealed class CriminalRecordsSystem : SharedCriminalRecordsSystem
if (index >= record.History.Count)
return false;
var history = record.History[(int)index];
record.History.RemoveAt((int) index);
var args = new CriminalHistoryRemovedEvent(history);
var query = EntityQueryEnumerator<WantedListCartridgeComponent>();
while (query.MoveNext(out var readerUid, out _))
{
RaiseLocalEvent(readerUid, ref args);
}
return true;
}
private void OnRecordChanged(Entity<WantedListCartridgeComponent> ent, ref CriminalRecordChangedEvent args) =>
StateChanged(ent);
private void OnHistoryAdded(Entity<WantedListCartridgeComponent> ent, ref CriminalHistoryAddedEvent args) =>
StateChanged(ent);
private void OnHistoryRemoved(Entity<WantedListCartridgeComponent> ent, ref CriminalHistoryRemovedEvent args) =>
StateChanged(ent);
private void StateChanged(Entity<WantedListCartridgeComponent> ent)
{
if (Comp<CartridgeComponent>(ent).LoaderUid is not { } loaderUid)
return;
UpdateReaderUi(ent, loaderUid);
}
private void OnCartridgeUiReady(Entity<WantedListCartridgeComponent> ent, ref CartridgeUiReadyEvent args)
{
UpdateReaderUi(ent, args.Loader);
}
private void UpdateReaderUi(Entity<WantedListCartridgeComponent> ent, EntityUid loaderUid)
{
if (_station.GetOwningStation(ent) is not { } station)
return;
var records = _records.GetRecordsOfType<CriminalRecord>(station)
.Where(cr => cr.Item2.Status is not SecurityStatus.None || cr.Item2.History.Count > 0)
.Select(cr =>
{
var (i, r) = cr;
var key = new StationRecordKey(i, station);
// Hopefully it will work smoothly.....
_records.TryGetRecord(key, out GeneralStationRecord? generalRecord);
return new WantedRecord(generalRecord!, r.Status, r.Reason, r.InitiatorName, r.History);
});
var state = new WantedListUiState(records.ToList());
_cartridge.UpdateCartridgeUiState(loaderUid, state);
}
}

View File

@@ -1,5 +1,6 @@
using Content.Server.Objectives.Components;
using Content.Server.Objectives.Components.Targets;
using Content.Shared.CartridgeLoader;
using Content.Shared.Mind;
using Content.Shared.Objectives.Components;
using Content.Shared.Objectives.Systems;
@@ -172,6 +173,11 @@ public sealed class StealConditionSystem : EntitySystem
if (target.StealGroup != condition.StealGroup)
return 0;
// check if cartridge is installed
if (TryComp<CartridgeComponent>(entity, out var cartridge) &&
cartridge.InstallationStatus is not InstallationStatus.Cartridge)
return 0;
// check if needed target alive
if (condition.CheckAlive)
{

View File

@@ -0,0 +1,11 @@
using Content.Shared.CriminalRecords;
using Content.Shared.CriminalRecords.Systems;
using Robust.Shared.Serialization;
namespace Content.Shared.CartridgeLoader.Cartridges;
[Serializable, NetSerializable]
public sealed class WantedListUiState(List<WantedRecord> records) : BoundUserInterfaceState
{
public List<WantedRecord> Records = records;
}

View File

@@ -23,6 +23,12 @@ public sealed record CriminalRecord
[DataField]
public string? Reason;
/// <summary>
/// The name of the person who changed the status.
/// </summary>
[DataField]
public string? InitiatorName;
/// <summary>
/// Criminal history of the person.
/// This should have charges and time served added after someone is detained.
@@ -35,4 +41,4 @@ public sealed record CriminalRecord
/// A line of criminal activity and the time it was added at.
/// </summary>
[Serializable, NetSerializable]
public record struct CrimeHistory(TimeSpan AddTime, string Crime);
public record struct CrimeHistory(TimeSpan AddTime, string Crime, string? InitiatorName);

View File

@@ -2,6 +2,8 @@ using Content.Shared.IdentityManagement;
using Content.Shared.IdentityManagement.Components;
using Content.Shared.Security;
using Content.Shared.Security.Components;
using Content.Shared.StationRecords;
using Robust.Shared.Serialization;
namespace Content.Shared.CriminalRecords.Systems;
@@ -50,3 +52,22 @@ public abstract class SharedCriminalRecordsSystem : EntitySystem
Dirty(characterUid, record);
}
}
[Serializable, NetSerializable]
public struct WantedRecord(GeneralStationRecord targetInfo, SecurityStatus status, string? reason, string? initiator, List<CrimeHistory> history)
{
public GeneralStationRecord TargetInfo = targetInfo;
public SecurityStatus Status = status;
public string? Reason = reason;
public string? Initiator = initiator;
public List<CrimeHistory> History = history;
};
[ByRefEvent]
public record struct CriminalRecordChangedEvent(CriminalRecord Record);
[ByRefEvent]
public record struct CriminalHistoryAddedEvent(CrimeHistory History);
[ByRefEvent]
public record struct CriminalHistoryRemovedEvent(CrimeHistory History);

View File

@@ -19,3 +19,32 @@ log-probe-scan = Downloaded logs from {$device}!
log-probe-label-time = Time
log-probe-label-accessor = Accessed by
log-probe-label-number = #
# Wanted list cartridge
wanted-list-program-name = Wanted list
wanted-list-label-no-records = It's all right, cowboy
wanted-list-search-placeholder = Search by name and status
wanted-list-age-label = [color=darkgray]Age:[/color] [color=white]{$age}[/color]
wanted-list-job-label = [color=darkgray]Job:[/color] [color=white]{$job}[/color]
wanted-list-species-label = [color=darkgray]Species:[/color] [color=white]{$species}[/color]
wanted-list-gender-label = [color=darkgray]Gender:[/color] [color=white]{$gender}[/color]
wanted-list-reason-label = [color=darkgray]Reason:[/color] [color=white]{$reason}[/color]
wanted-list-unknown-reason-label = unknown reason
wanted-list-initiator-label = [color=darkgray]Initiator:[/color] [color=white]{$initiator}[/color]
wanted-list-unknown-initiator-label = unknown initiator
wanted-list-status-label = [color=darkgray]status:[/color] {$status ->
[suspected] [color=yellow]suspected[/color]
[wanted] [color=red]wanted[/color]
[detained] [color=#b18644]detained[/color]
[paroled] [color=green]paroled[/color]
[discharged] [color=green]discharged[/color]
*[other] none
}
wanted-list-history-table-time-col = Time
wanted-list-history-table-reason-col = Crime
wanted-list-history-table-initiator-col = Initiator

View File

@@ -39,7 +39,7 @@ criminal-records-console-released = {$name} has been released by {$officer}.
criminal-records-console-not-wanted = {$officer} cleared the wanted status of {$name}.
criminal-records-console-paroled = {$name} has been released on parole by {$officer}.
criminal-records-console-not-parole = {$officer} cleared the parole status of {$name}.
criminal-records-console-unknown-officer = <unknown officer>
criminal-records-console-unknown-officer = <unknown>
## Filters

View File

@@ -40,6 +40,7 @@ steal-target-groups-clothing-eyes-hud-beer = beer goggles
steal-target-groups-bible = bible
steal-target-groups-clothing-neck-goldmedal = gold medal of crewmanship
steal-target-groups-clothing-neck-clownmedal = clown medal
steal-target-groups-wanted-list-cartridge = wanted list cartridge
# Thief structures
steal-target-groups-teg = teg generator part

View File

@@ -325,6 +325,7 @@
- id: RubberStampHos
- id: SecurityTechFabCircuitboard
- id: WeaponDisabler
- id: WantedListCartridge
# Hardsuit table, used for suit storage as well
- type: entityTable

View File

@@ -93,3 +93,26 @@
- type: GuideHelp
guides:
- Forensics
- type: entity
parent: BaseItem
id: WantedListCartridge
name: Wanted list cartridge
description: A program to get a list of wanted persons.
components:
- type: Sprite
sprite: Objects/Devices/cartridge.rsi
state: cart-sec
- type: Icon
sprite: Objects/Devices/cartridge.rsi
state: cart-sec
- type: UIFragment
ui: !type:WantedListUi
- type: Cartridge
programName: wanted-list-program-name
icon:
sprite: Objects/Misc/books.rsi
state: icon_magnifier
- type: WantedListCartridge
- type: StealTarget
stealGroup: WantedListCartridge

View File

@@ -114,6 +114,18 @@
- type: Speech
speechVerb: Robotic
- type: entity
id: BaseSecurityPDA
abstract: true
components:
- type: CartridgeLoader
uiKey: enum.PdaUiKey.Key
preinstalled:
- CrewManifestCartridge
- NotekeeperCartridge
- NewsReaderCartridge
- WantedListCartridge
- type: entity
parent: BasePDA
id: BaseMedicalPDA
@@ -433,7 +445,7 @@
state: pda-library
- type: entity
parent: BasePDA
parent: [BaseSecurityPDA, BasePDA]
id: LawyerPDA
name: lawyer PDA
description: For lawyers to poach dubious clients.
@@ -476,7 +488,7 @@
state: pda-janitor
- type: entity
parent: BasePDA
parent: [BaseSecurityPDA, BasePDA]
id: CaptainPDA
name: captain PDA
description: Surprisingly no different from your PDA.
@@ -651,7 +663,7 @@
state: pda-science
- type: entity
parent: BasePDA
parent: [BaseSecurityPDA, BasePDA]
id: HoSPDA
name: head of security PDA
description: Whosoever bears this PDA is the law.
@@ -666,7 +678,7 @@
state: pda-hos
- type: entity
parent: BasePDA
parent: [BaseSecurityPDA, BasePDA]
id: WardenPDA
name: warden PDA
description: The OS appears to have been jailbroken.
@@ -681,7 +693,7 @@
state: pda-warden
- type: entity
parent: BasePDA
parent: [BaseSecurityPDA, BasePDA]
id: SecurityPDA
name: security PDA
description: Red to hide the stains of passenger blood.
@@ -695,7 +707,7 @@
state: pda-security
- type: entity
parent: BasePDA
parent: [BaseSecurityPDA, BasePDA]
id: CentcomPDA
name: CentComm PDA
description: Light green sign of walking bureaucracy.
@@ -733,6 +745,7 @@
- NotekeeperCartridge
- NewsReaderCartridge
- LogProbeCartridge
- WantedListCartridge
- type: entity
parent: CentcomPDA
@@ -834,7 +847,7 @@
- Cartridge
- type: entity
parent: BasePDA
parent: [BaseSecurityPDA, BasePDA]
id: ERTLeaderPDA
name: ERT Leader PDA
suffix: Leader
@@ -982,7 +995,7 @@
state: pda-boxer
- type: entity
parent: BasePDA
parent: [BaseSecurityPDA, BasePDA]
id: DetectivePDA
name: detective PDA
description: Smells like rain... pouring down the rooftops...
@@ -1082,7 +1095,7 @@
state: pda-seniorphysician
- type: entity
parent: BasePDA
parent: [BaseSecurityPDA, BasePDA]
id: SeniorOfficerPDA
name: senior officer PDA
description: Beaten, battered and broken, but just barely useable.

View File

@@ -73,6 +73,7 @@
ForensicScannerStealObjective: 1 #sec
FlippoEngravedLighterStealObjective: 0.5
ClothingHeadHatWardenStealObjective: 1
WantedListCartridgeStealObjective: 1
ClothingOuterHardsuitVoidParamedStealObjective: 1 #med
MedicalTechFabCircuitboardStealObjective: 1
ClothingHeadsetAltMedicalStealObjective: 1

View File

@@ -263,6 +263,13 @@
sprite: Clothing/Neck/Medals/clownmedal.rsi
state: icon
- type: stealTargetGroup
id: WantedListCartridge
name: steal-target-groups-wanted-list-cartridge
sprite:
sprite: Objects/Devices/cartridge.rsi
state: cart-sec
#Thief structures
- type: stealTargetGroup

View File

@@ -184,6 +184,15 @@
- type: Objective
difficulty: 1.2
- type: entity
parent: BaseThiefStealObjective
id: WantedListCartridgeStealObjective
components:
- type: StealCondition
stealGroup: WantedListCartridge
- type: Objective
difficulty: 1
- type: entity #Medical subgroup
parent: BaseThiefStealObjective
id: ClothingOuterHardsuitVoidParamedStealObjective

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

View File

@@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from vgstation at https://github.com/vgstation-coders/vgstation13/commit/1cdfb0230cc96d0ba751fa002d04f8aa2f25ad7d and tgstation at tgstation at https://github.com/tgstation/tgstation/commit/0c15d9dbcf0f2beb230eba5d9d889ef2d1945bb8, cart-log made by Skarletto (github)",
"copyright": "Taken from vgstation at https://github.com/vgstation-coders/vgstation13/commit/1cdfb0230cc96d0ba751fa002d04f8aa2f25ad7d and tgstation at tgstation at https://github.com/tgstation/tgstation/commit/0c15d9dbcf0f2beb230eba5d9d889ef2d1945bb8, cart-log made by Skarletto (github), cart-sec made by dieselmohawk (discord)",
"size": {
"x": 32,
"y": 32
@@ -79,6 +79,9 @@
{
"name": "cart-y"
},
{
"name": "cart-sec"
},
{
"name": "insert_overlay"
}