Station records (#8720)

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
Flipp Syder
2022-08-08 22:10:01 -07:00
committed by GitHub
parent 75dfbdb57f
commit 3d36a6e1f6
35 changed files with 1888 additions and 9 deletions

View File

@@ -0,0 +1,75 @@
using Content.Server.Station.Systems;
using Content.Shared.StationRecords;
using Robust.Server.GameObjects;
namespace Content.Server.StationRecords.Systems;
public sealed class GeneralStationRecordConsoleSystem : EntitySystem
{
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
[Dependency] private readonly StationSystem _stationSystem = default!;
[Dependency] private readonly StationRecordsSystem _stationRecordsSystem = default!;
public override void Initialize()
{
SubscribeLocalEvent<GeneralStationRecordConsoleComponent, BoundUIOpenedEvent>(UpdateUserInterface);
SubscribeLocalEvent<GeneralStationRecordConsoleComponent, SelectGeneralStationRecord>(OnKeySelected);
SubscribeLocalEvent<GeneralStationRecordConsoleComponent, RecordModifiedEvent>(UpdateUserInterface);
SubscribeLocalEvent<GeneralStationRecordConsoleComponent, AfterGeneralRecordCreatedEvent>(UpdateUserInterface);
}
private void UpdateUserInterface<T>(EntityUid uid, GeneralStationRecordConsoleComponent component, T ev)
{
UpdateUserInterface(uid, component);
}
private void OnKeySelected(EntityUid uid, GeneralStationRecordConsoleComponent component,
SelectGeneralStationRecord msg)
{
component.ActiveKey = msg.SelectedKey;
UpdateUserInterface(uid, component);
}
private void UpdateUserInterface(EntityUid uid, GeneralStationRecordConsoleComponent? console = null)
{
if (!Resolve(uid, ref console))
{
return;
}
var owningStation = _stationSystem.GetOwningStation(uid);
if (!TryComp<StationRecordsComponent>(owningStation, out var stationRecordsComponent))
{
_userInterface.GetUiOrNull(uid, GeneralStationRecordConsoleKey.Key)?.SetState(new GeneralStationRecordConsoleState(null, null, null));
return;
}
var enumerator = _stationRecordsSystem.GetRecordsOfType<GeneralStationRecord>(owningStation.Value, stationRecordsComponent);
var listing = new Dictionary<StationRecordKey, string>();
foreach (var pair in enumerator)
{
listing.Add(pair.Item1, pair.Item2.Name);
}
if (listing.Count == 0)
{
_userInterface.GetUiOrNull(uid, GeneralStationRecordConsoleKey.Key)?.SetState(new GeneralStationRecordConsoleState(null, null, null));
return;
}
GeneralStationRecord? record = null;
if (console.ActiveKey != null)
{
_stationRecordsSystem.TryGetRecord(owningStation.Value, console.ActiveKey.Value, out record,
stationRecordsComponent);
}
_userInterface
.GetUiOrNull(uid, GeneralStationRecordConsoleKey.Key)?
.SetState(new GeneralStationRecordConsoleState(console.ActiveKey, record, listing));
}
}

View File

@@ -0,0 +1,57 @@
using Content.Shared.StationRecords;
namespace Content.Server.StationRecords.Systems;
public sealed class StationRecordKeyStorageSystem : EntitySystem
{
/// <summary>
/// Assigns a station record key to an entity.
/// </summary>
/// <param name="uid"></param>
/// <param name="key"></param>
/// <param name="keyStorage"></param>
public void AssignKey(EntityUid uid, StationRecordKey key, StationRecordKeyStorageComponent? keyStorage = null)
{
if (!Resolve(uid, ref keyStorage))
{
return;
}
keyStorage.Key = key;
}
/// <summary>
/// Removes a station record key from an entity.
/// </summary>
/// <param name="uid"></param>
/// <param name="keyStorage"></param>
/// <returns></returns>
public StationRecordKey? RemoveKey(EntityUid uid, StationRecordKeyStorageComponent? keyStorage = null)
{
if (!Resolve(uid, ref keyStorage) || keyStorage.Key == null)
{
return null;
}
var key = keyStorage.Key;
keyStorage.Key = null;
return key;
}
/// <summary>
/// Checks if an entity currently contains a station record key.
/// </summary>
/// <param name="uid"></param>
/// <param name="keyStorage"></param>
/// <returns></returns>
public bool CheckKey(EntityUid uid, StationRecordKeyStorageComponent? keyStorage = null)
{
if (!Resolve(uid, ref keyStorage))
{
return false;
}
return keyStorage.Key != null;
}
}

View File

@@ -0,0 +1,282 @@
using System.Diagnostics.CodeAnalysis;
using Content.Server.Access.Systems;
using Content.Server.GameTicking;
using Content.Server.Station.Systems;
using Content.Server.StationRecords;
using Content.Server.StationRecords.Systems;
using Content.Shared.Access.Components;
using Content.Shared.Inventory;
using Content.Shared.PDA;
using Content.Shared.Preferences;
using Content.Shared.Roles;
using Content.Shared.StationRecords;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
/// <summary>
/// Station records.
///
/// A station record is tied to an ID card, or anything that holds
/// a station record's key. This key will determine access to a
/// station record set's record entries, and it is imperative not
/// to lose the item that holds the key under any circumstance.
///
/// Records are mostly a roleplaying tool, but can have some
/// functionality as well (i.e., security records indicating that
/// a specific person holding an ID card with a linked key is
/// currently under warrant, showing a crew manifest with user
/// settable, custom titles).
///
/// General records are tied into this system, as most crewmembers
/// should have a general record - and most systems should probably
/// depend on this general record being created. This is subject
/// to change.
/// </summary>
public sealed class StationRecordsSystem : EntitySystem
{
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly StationRecordKeyStorageSystem _keyStorageSystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<StationInitializedEvent>(OnStationInitialize);
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnPlayerSpawn);
}
private void OnStationInitialize(StationInitializedEvent args)
{
AddComp<StationRecordsComponent>(args.Station);
}
private void OnPlayerSpawn(PlayerSpawnCompleteEvent args)
{
CreateGeneralRecord(args.Station, args.Mob, args.Profile, args.JobId);
}
private void CreateGeneralRecord(EntityUid station, EntityUid player, HumanoidCharacterProfile profile,
string? jobId, StationRecordsComponent? records = null)
{
if (!Resolve(station, ref records)
|| String.IsNullOrEmpty(jobId)
|| !_prototypeManager.HasIndex<JobPrototype>(jobId))
{
return;
}
if (!_inventorySystem.TryGetSlotEntity(player, "id", out var idUid))
{
return;
}
CreateGeneralRecord(station, idUid.Value, profile.Name, profile.Age, profile.Species, profile.Gender, jobId, profile, records);
}
/// <summary>
/// Create a general record to store in a station's record set.
/// </summary>
/// <remarks>
/// This is tied into the record system, as any crew member's
/// records should generally be dependent on some generic
/// record with the bare minimum of information involved.
/// </remarks>
/// <param name="station">The entity uid of the station.</param>
/// <param name="idUid">The entity uid of an entity's ID card. Can be null.</param>
/// <param name="name">Name of the character.</param>
/// <param name="species">Species of the character.</param>
/// <param name="gender">Gender of the character.</param>
/// <param name="jobId">
/// The job to initially tie this record to. This must be a valid job loaded in, otherwise
/// this call will cause an exception. Ensure that a general record starts out with a job
/// that is currently a valid job prototype.
/// </param>
/// <param name="profile">
/// Profile for the related player. This is so that other systems can get further information
/// about the player character.
/// Optional - other systems should anticipate this.
/// </param>
/// <param name="records">Station records component.</param>
public void CreateGeneralRecord(EntityUid station, EntityUid? idUid, string name, int age, string species, Gender gender, string jobId, HumanoidCharacterProfile? profile = null,
StationRecordsComponent? records = null)
{
if (!Resolve(station, ref records))
{
return;
}
if (!_prototypeManager.TryIndex(jobId, out JobPrototype? jobPrototype))
{
throw new ArgumentException($"Invalid job prototype ID: {jobId}");
}
var record = new GeneralStationRecord()
{
Name = name,
Age = age,
JobTitle = jobPrototype.Name,
JobIcon = jobPrototype.Icon,
JobPrototype = jobId,
Species = species,
Gender = gender,
DisplayPriority = jobPrototype.Weight
};
var key = records.Records.AddRecord(station);
records.Records.AddRecordEntry(key, record);
// entry.Entries.Add(typeof(GeneralStationRecord), record);
if (idUid != null)
{
var keyStorageEntity = idUid;
if (TryComp(idUid, out PDAComponent? pdaComponent) && pdaComponent.ContainedID != null)
{
keyStorageEntity = pdaComponent.IdSlot.Item;
}
if (keyStorageEntity != null)
{
_keyStorageSystem.AssignKey(keyStorageEntity.Value, key);
}
}
RaiseLocalEvent(new AfterGeneralRecordCreatedEvent(key, record, profile));
}
/// <summary>
/// Removes a record from this station.
/// </summary>
/// <param name="station">Station to remove the record from.</param>
/// <param name="key">The key to remove.</param>
/// <param name="records">Station records component.</param>
/// <returns>True if the record was removed, false otherwise.</returns>
public bool RemoveRecord(EntityUid station, StationRecordKey key, StationRecordsComponent? records = null)
{
if (station != key.OriginStation || !Resolve(station, ref records))
{
return false;
}
RaiseLocalEvent(new RecordRemovedEvent(key));
return records.Records.RemoveAllRecords(key);
}
/// <summary>
/// Try to get a record from this station's record entries,
/// from the provided station record key. Will always return
/// null if the key does not match the station.
/// </summary>
/// <param name="station">Station to get the record from.</param>
/// <param name="key">Key to try and index from the record set.</param>
/// <param name="entry">The resulting entry.</param>
/// <param name="records">Station record component.</param>
/// <typeparam name="T">Type to get from the record set.</typeparam>
/// <returns>True if the record was obtained, false otherwise.</returns>
public bool TryGetRecord<T>(EntityUid station, StationRecordKey key, [NotNullWhen(true)] out T? entry, StationRecordsComponent? records = null)
{
entry = default;
if (key.OriginStation != station || !Resolve(station, ref records))
{
return false;
}
return records.Records.TryGetRecordEntry(key, out entry);
}
/// <summary>
/// Gets all records of a specific type from a station.
/// </summary>
/// <param name="station">The station to get the records from.</param>
/// <param name="records">Station records component.</param>
/// <typeparam name="T">Type of record to fetch</typeparam>
/// <returns>Enumerable of pairs with a station record key, and the entry in question of type T.</returns>
public IEnumerable<(StationRecordKey, T)> GetRecordsOfType<T>(EntityUid station, StationRecordsComponent? records = null)
{
if (!Resolve(station, ref records))
{
return new (StationRecordKey, T)[]{};
}
return records.Records.GetRecordsOfType<T>();
}
/// <summary>
/// Synchronizes a station's records with any systems that need it.
/// </summary>
/// <param name="station">The station to synchronize any recently accessed records with..</param>
/// <param name="records">Station records component.</param>
public void Synchronize(EntityUid station, StationRecordsComponent? records = null)
{
if (!Resolve(station, ref records))
{
return;
}
foreach (var key in records.Records.GetRecentlyAccessed())
{
RaiseLocalEvent(new RecordModifiedEvent(key));
}
records.Records.ClearRecentlyAccessed();
}
}
/// <summary>
/// Event raised after the player's general profile is created.
/// Systems that modify records on a station would have more use
/// listening to this event, as it contains the character's record key.
/// Also stores the general record reference, to save some time.
/// </summary>
public sealed class AfterGeneralRecordCreatedEvent : EntityEventArgs
{
public StationRecordKey Key { get; }
public GeneralStationRecord Record { get; }
/// <summary>
/// Profile for the related player. This is so that other systems can get further information
/// about the player character.
/// Optional - other systems should anticipate this.
/// </summary>
public HumanoidCharacterProfile? Profile { get; }
public AfterGeneralRecordCreatedEvent(StationRecordKey key, GeneralStationRecord record, HumanoidCharacterProfile? profile)
{
Key = key;
Record = record;
Profile = profile;
}
}
/// <summary>
/// Event raised after a record is removed. Only the key is given
/// when the record is removed, so that any relevant systems/components
/// that store record keys can then remove the key from their internal
/// fields.
/// </summary>
public sealed class RecordRemovedEvent : EntityEventArgs
{
public StationRecordKey Key { get; }
public RecordRemovedEvent(StationRecordKey key)
{
Key = key;
}
}
/// <summary>
/// Event raised after a record is modified. This is to
/// inform other systems that records stored in this key
/// may have changed.
/// </summary>
public sealed class RecordModifiedEvent : EntityEventArgs
{
public StationRecordKey Key { get; }
public RecordModifiedEvent(StationRecordKey key)
{
Key = key;
}
}