using System.Linq; using System.Text.Json.Serialization; using Robust.Shared.Serialization; namespace Content.Shared._CD.Records; /// /// Contains Cosmatic Drift records that can be changed in the character editor. This is stored on the character's profile. /// [DataDefinition] [Serializable, NetSerializable] public sealed partial class PlayerProvidedCharacterRecords { public const int TextMedLen = 64; public const int TextVeryLargeLen = 4096; /* Basic info */ // Additional data is fetched from the Profile // All [DataField] public int Height { get; private set; } public const int MaxHeight = 800; [DataField] public int Weight { get; private set; } public const int MaxWeight = 300; [DataField] public string EmergencyContactName { get; private set; } // Employment [DataField] public bool HasWorkAuthorization { get; private set; } // Security [DataField] public string IdentifyingFeatures { get; private set; } // Medical [DataField] public string Allergies { get; private set; } [DataField] public string DrugAllergies { get; private set; } [DataField] public string PostmortemInstructions { get; private set; } // history, prescriptions, etc. would be a record below // "incidents" [DataField, JsonIgnore] public List MedicalEntries { get; private set; } [DataField, JsonIgnore] public List SecurityEntries { get; private set; } [DataField, JsonIgnore] public List EmploymentEntries { get; private set; } [DataDefinition] [Serializable, NetSerializable] public sealed partial class RecordEntry { [DataField] public string Title { get; private set; } // players involved, can be left blank (or with a generic "CentCom" etc.) for backstory related issues [DataField] public string Involved { get; private set; } // Longer description of events. [DataField] public string Description { get; private set; } public RecordEntry(string title, string involved, string desc) { Title = title; Involved = involved; Description = desc; } public RecordEntry(RecordEntry other) : this(other.Title, other.Involved, other.Description) { } public bool MemberwiseEquals(RecordEntry other) { return Title == other.Title && Involved == other.Involved && Description == other.Description; } public void EnsureValid() { Title = ClampString(Title, TextMedLen); Involved = ClampString(Involved, TextMedLen); Description = ClampString(Description, TextVeryLargeLen); } } public PlayerProvidedCharacterRecords( bool hasWorkAuthorization, int height, int weight, string emergencyContactName, string identifyingFeatures, string allergies, string drugAllergies, string postmortemInstructions, List medicalEntries, List securityEntries, List employmentEntries) { HasWorkAuthorization = hasWorkAuthorization; Height = height; Weight = weight; EmergencyContactName = emergencyContactName; IdentifyingFeatures = identifyingFeatures; Allergies = allergies; DrugAllergies = drugAllergies; PostmortemInstructions = postmortemInstructions; MedicalEntries = medicalEntries; SecurityEntries = securityEntries; EmploymentEntries = employmentEntries; } public PlayerProvidedCharacterRecords(PlayerProvidedCharacterRecords other) { Height = other.Height; Weight = other.Weight; EmergencyContactName = other.EmergencyContactName; HasWorkAuthorization = other.HasWorkAuthorization; IdentifyingFeatures = other.IdentifyingFeatures; Allergies = other.Allergies; DrugAllergies = other.DrugAllergies; PostmortemInstructions = other.PostmortemInstructions; MedicalEntries = other.MedicalEntries.Select(x => new RecordEntry(x)).ToList(); SecurityEntries = other.SecurityEntries.Select(x => new RecordEntry(x)).ToList(); EmploymentEntries = other.EmploymentEntries.Select(x => new RecordEntry(x)).ToList(); } public static PlayerProvidedCharacterRecords DefaultRecords() { return new PlayerProvidedCharacterRecords( hasWorkAuthorization: true, height: 170, weight: 70, emergencyContactName: "", identifyingFeatures: "", allergies: "None", drugAllergies: "None", postmortemInstructions: "Return home", medicalEntries: new List(), securityEntries: new List(), employmentEntries: new List() ); } public bool MemberwiseEquals(PlayerProvidedCharacterRecords other) { // This is ugly but is only used for integration tests. var test = Height == other.Height && Weight == other.Weight && EmergencyContactName == other.EmergencyContactName && HasWorkAuthorization == other.HasWorkAuthorization && IdentifyingFeatures == other.IdentifyingFeatures && Allergies == other.Allergies && DrugAllergies == other.DrugAllergies && PostmortemInstructions == other.PostmortemInstructions; if (!test) return false; if (MedicalEntries.Count != other.MedicalEntries.Count) return false; if (SecurityEntries.Count != other.SecurityEntries.Count) return false; if (EmploymentEntries.Count != other.EmploymentEntries.Count) return false; if (MedicalEntries.Where((t, i) => !t.MemberwiseEquals(other.MedicalEntries[i])).Any()) { return false; } if (SecurityEntries.Where((t, i) => !t.MemberwiseEquals(other.SecurityEntries[i])).Any()) { return false; } if (EmploymentEntries.Where((t, i) => !t.MemberwiseEquals(other.EmploymentEntries[i])).Any()) { return false; } return true; } private static string ClampString(string str, int maxLen) { if (str.Length > maxLen) { return str[..maxLen]; } return str; } private static void EnsureValidEntries(List entries) { foreach (var entry in entries) { entry.EnsureValid(); } } /// /// Clamp invalid entries to valid values /// public void EnsureValid() { Height = Math.Clamp(Height, 0, MaxHeight); Weight = Math.Clamp(Weight, 0, MaxWeight); EmergencyContactName = ClampString(EmergencyContactName, TextMedLen); IdentifyingFeatures = ClampString(IdentifyingFeatures, TextMedLen); Allergies = ClampString(Allergies, TextMedLen); DrugAllergies = ClampString(DrugAllergies, TextMedLen); PostmortemInstructions = ClampString(PostmortemInstructions, TextMedLen); EnsureValidEntries(EmploymentEntries); EnsureValidEntries(MedicalEntries); EnsureValidEntries(SecurityEntries); } public PlayerProvidedCharacterRecords WithHeight(int height) { return new(this) { Height = height }; } public PlayerProvidedCharacterRecords WithWeight(int weight) { return new(this) { Weight = weight }; } public PlayerProvidedCharacterRecords WithWorkAuth(bool auth) { return new(this) { HasWorkAuthorization = auth }; } public PlayerProvidedCharacterRecords WithContactName(string name) { return new(this) { EmergencyContactName = name}; } public PlayerProvidedCharacterRecords WithIdentifyingFeatures(string feat) { return new(this) { IdentifyingFeatures = feat}; } public PlayerProvidedCharacterRecords WithAllergies(string s) { return new(this) { Allergies = s }; } public PlayerProvidedCharacterRecords WithDrugAllergies(string s) { return new(this) { DrugAllergies = s }; } public PlayerProvidedCharacterRecords WithPostmortemInstructions(string s) { return new(this) { PostmortemInstructions = s}; } public PlayerProvidedCharacterRecords WithEmploymentEntries(List entries) { return new(this) { EmploymentEntries = entries}; } public PlayerProvidedCharacterRecords WithMedicalEntries(List entries) { return new(this) { MedicalEntries = entries}; } public PlayerProvidedCharacterRecords WithSecurityEntries(List entries) { return new(this) { SecurityEntries = entries}; } } public enum CharacterRecordType : byte { Employment, Medical, Security }