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; /// /// Criminal records /// /// Criminal Records inherit Station Records' core and add role-playing tools for Security: /// - Ability to track a person's status (Detained/Wanted/None) /// - See security officers' actions in Criminal Records in the radio /// - See reasons for any action with no need to ask the officer personally /// 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(OnGeneralRecordCreated); SubscribeLocalEvent(OnRecordChanged); SubscribeLocalEvent(OnCartridgeUiReady); SubscribeLocalEvent(OnHistoryAdded); SubscribeLocalEvent(OnHistoryRemoved); } private void OnGeneralRecordCreated(AfterGeneralRecordCreatedEvent ev) { _records.AddRecordEntry(ev.Key, new CriminalRecord()); _records.Synchronize(ev.Key); } /// /// Tries to change the status of the record found by the StationRecordKey. /// Reason should only be passed if status is Wanted, nullability isn't checked. /// /// True if the status is changed, false if not public bool TryChangeStatus(StationRecordKey key, SecurityStatus status, string? reason, string? initiatorName = null) { // don't do anything if its the same status if (!_records.TryGetRecord(key, out var record) || status == record.Status) return false; OverwriteStatus(key, record, status, reason, initiatorName); return true; } /// /// Sets the status without checking previous status or reason nullability. /// 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(); while (query.MoveNext(out var readerUid, out _)) { RaiseLocalEvent(readerUid, ref args); } } /// /// Tries to add a history entry to a criminal record. /// /// True if adding succeeded, false if not public bool TryAddHistory(StationRecordKey key, CrimeHistory entry) { if (!_records.TryGetRecord(key, out var record)) return false; record.History.Add(entry); var args = new CriminalHistoryAddedEvent(entry); var query = EntityQueryEnumerator(); while (query.MoveNext(out var readerUid, out _)) { RaiseLocalEvent(readerUid, ref args); } return true; } /// /// Creates and tries to add a history entry using the current time. /// public bool TryAddHistory(StationRecordKey key, string line, string? initiatorName = null) { var entry = new CrimeHistory(_ticker.RoundDuration(), line, initiatorName); return TryAddHistory(key, entry); } /// /// Tries to delete a sepcific line of history from a criminal record, by index. /// /// True if the line was removed, false if not public bool TryDeleteHistory(StationRecordKey key, uint index) { if (!_records.TryGetRecord(key, out var record)) return false; 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(); while (query.MoveNext(out var readerUid, out _)) { RaiseLocalEvent(readerUid, ref args); } return true; } private void OnRecordChanged(Entity ent, ref CriminalRecordChangedEvent args) => StateChanged(ent); private void OnHistoryAdded(Entity ent, ref CriminalHistoryAddedEvent args) => StateChanged(ent); private void OnHistoryRemoved(Entity ent, ref CriminalHistoryRemovedEvent args) => StateChanged(ent); private void StateChanged(Entity ent) { if (Comp(ent).LoaderUid is not { } loaderUid) return; UpdateReaderUi(ent, loaderUid); } private void OnCartridgeUiReady(Entity ent, ref CartridgeUiReadyEvent args) { UpdateReaderUi(ent, args.Loader); } private void UpdateReaderUi(Entity ent, EntityUid loaderUid) { if (_station.GetOwningStation(ent) is not { } station) return; var records = _records.GetRecordsOfType(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); } }