From 9cc4a50692c4ea354a43683a9e6d404e31ed97a3 Mon Sep 17 00:00:00 2001 From: themias <89101928+themias@users.noreply.github.com> Date: Fri, 15 Dec 2023 04:52:55 -0500 Subject: [PATCH] Add more DNA interactions (#21989) * Add more DNA interactions * remove unused import * update based on feedback * Add event for chemistrysystem.injector * move event to shared; transfer dna to implanter * doafter and interaction event fixes * add BreakOnHandChange * doh * use events instead of updating component directly * Add DataFields to ForensicScannerComponent fields * Convert most events to system api call --- .../Forensics/ForensicScannerMenu.xaml.cs | 6 + .../Body/Systems/BloodstreamSystem.cs | 16 +-- .../EntitySystems/ChemistrySystem.Injector.cs | 21 +++- .../EntitySystems/ChemistrySystemHypospray.cs | 4 + .../Components/ForensicScannerComponent.cs | 12 +- .../Components/ForensicsComponent.cs | 22 ++++ .../Forensics/Components/ResidueComponent.cs | 15 +++ .../Systems/ForensicScannerSystem.cs | 9 ++ .../Forensics/Systems/ForensicsSystem.cs | 115 ++++++++++++++++++ Content.Server/Medical/VomitSystem.cs | 5 +- .../Nutrition/EntitySystems/DrinkSystem.cs | 5 +- .../Nutrition/EntitySystems/SmokingSystem.cs | 12 ++ Content.Shared/Forensics/Events.cs | 27 ++++ .../Forensics/ForensicScannerEvent.cs | 3 + .../Implants/SharedImplanterSystem.cs | 8 ++ .../Locale/en-US/forensics/forensics.ftl | 1 + Resources/Locale/en-US/forensics/residues.ftl | 11 ++ .../Objects/Specific/Janitorial/janitor.yml | 3 + .../Objects/Specific/Janitorial/soap.yml | 19 +++ Resources/Prototypes/tags.yml | 3 + 20 files changed, 291 insertions(+), 26 deletions(-) create mode 100644 Content.Server/Forensics/Components/ResidueComponent.cs create mode 100644 Resources/Locale/en-US/forensics/residues.ftl diff --git a/Content.Client/Forensics/ForensicScannerMenu.xaml.cs b/Content.Client/Forensics/ForensicScannerMenu.xaml.cs index 84ffd7969e..8b6152c861 100644 --- a/Content.Client/Forensics/ForensicScannerMenu.xaml.cs +++ b/Content.Client/Forensics/ForensicScannerMenu.xaml.cs @@ -58,6 +58,12 @@ namespace Content.Client.Forensics { text.AppendLine(dna); } + text.AppendLine(); + text.AppendLine(Loc.GetString("forensic-scanner-interface-residues")); + foreach (var residue in msg.Residues) + { + text.AppendLine(residue); + } Diagnostics.Text = text.ToString(); } } diff --git a/Content.Server/Body/Systems/BloodstreamSystem.cs b/Content.Server/Body/Systems/BloodstreamSystem.cs index f1ab5702e5..cb83625615 100644 --- a/Content.Server/Body/Systems/BloodstreamSystem.cs +++ b/Content.Server/Body/Systems/BloodstreamSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Body.Components; using Content.Server.Chemistry.ReactionEffects; using Content.Server.Fluids.EntitySystems; -using Content.Server.Forensics; using Content.Server.HealthExaminable; using Content.Server.Popups; using Content.Shared.Alert; @@ -22,6 +21,8 @@ using Robust.Shared.Prototypes; using Robust.Shared.Random; using Content.Shared.Speech.EntitySystems; using Robust.Server.Audio; +using Robust.Shared.GameObjects; +using Content.Server.Forensics; namespace Content.Server.Body.Systems; @@ -38,6 +39,7 @@ public sealed class BloodstreamSystem : EntitySystem [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly SharedStutteringSystem _stutteringSystem = default!; [Dependency] private readonly AlertsSystem _alertsSystem = default!; + [Dependency] private readonly ForensicsSystem _forensicsSystem = default!; public override void Initialize() { @@ -322,11 +324,7 @@ public sealed class BloodstreamSystem : EntitySystem component.BloodTemporarySolution.AddSolution(temp, _prototypeManager); if (_puddleSystem.TrySpillAt(uid, component.BloodTemporarySolution, out var puddleUid, false)) { - if (TryComp(uid, out var dna)) - { - var comp = EnsureComp(puddleUid); - comp.DNAs.Add(dna.DNA); - } + _forensicsSystem.TransferDna(puddleUid, uid, false); } component.BloodTemporarySolution.RemoveAllSolution(); @@ -378,11 +376,7 @@ public sealed class BloodstreamSystem : EntitySystem if (_puddleSystem.TrySpillAt(uid, tempSol, out var puddleUid)) { - if (TryComp(uid, out var dna)) - { - var comp = EnsureComp(puddleUid); - comp.DNAs.Add(dna.DNA); - } + _forensicsSystem.TransferDna(puddleUid, uid, false); } } diff --git a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.Injector.cs b/Content.Server/Chemistry/EntitySystems/ChemistrySystem.Injector.cs index 6b085d133e..c618094d1b 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.Injector.cs +++ b/Content.Server/Chemistry/EntitySystems/ChemistrySystem.Injector.cs @@ -15,6 +15,7 @@ using Content.Shared.Mobs.Components; using Content.Shared.Verbs; using Content.Shared.Stacks; using Robust.Shared.Player; +using Content.Shared.Forensics; namespace Content.Server.Chemistry.EntitySystems; @@ -290,7 +291,7 @@ public sealed partial class ChemistrySystem ("target", Identity.Entity(target, EntityManager))), injector, user); Dirty(component); - AfterInject(component, injector); + AfterInject(component, injector, target); } private void TryInject(InjectorComponent component, EntityUid injector, EntityUid targetEntity, Solution targetSolution, EntityUid user, bool asRefill) @@ -328,10 +329,10 @@ public sealed partial class ChemistrySystem ("target", Identity.Entity(targetEntity, EntityManager))), injector, user); Dirty(component); - AfterInject(component, injector); + AfterInject(component, injector, targetEntity); } - private void AfterInject(InjectorComponent component, EntityUid injector) + private void AfterInject(InjectorComponent component, EntityUid injector, EntityUid target) { // Automatically set syringe to draw after completely draining it. if (_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution) @@ -339,9 +340,13 @@ public sealed partial class ChemistrySystem { component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Draw; } + + // Leave some DNA from the injectee on it + var ev = new TransferDnaEvent { Donor = target, Recipient = injector }; + RaiseLocalEvent(target, ref ev); } - private void AfterDraw(InjectorComponent component, EntityUid injector) + private void AfterDraw(InjectorComponent component, EntityUid injector, EntityUid target) { // Automatically set syringe to inject after completely filling it. if (_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution) @@ -349,6 +354,10 @@ public sealed partial class ChemistrySystem { component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Inject; } + + // Leave some DNA from the drawee on it + var ev = new TransferDnaEvent { Donor = target, Recipient = injector }; + RaiseLocalEvent(target, ref ev); } private void TryDraw(InjectorComponent component, EntityUid injector, EntityUid targetEntity, Solution targetSolution, EntityUid user, BloodstreamComponent? stream = null) @@ -389,7 +398,7 @@ public sealed partial class ChemistrySystem ("target", Identity.Entity(targetEntity, EntityManager))), injector, user); Dirty(component); - AfterDraw(component, injector); + AfterDraw(component, injector, targetEntity); } private void DrawFromBlood(EntityUid user, EntityUid injector, EntityUid target, InjectorComponent component, Solution injectorSolution, BloodstreamComponent stream, FixedPoint2 transferAmount) @@ -414,7 +423,7 @@ public sealed partial class ChemistrySystem ("target", Identity.Entity(target, EntityManager))), injector, user); Dirty(component); - AfterDraw(component, injector); + AfterDraw(component, injector, target); } } diff --git a/Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs b/Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs index fcc3c58fa7..cb4feb8064 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs +++ b/Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs @@ -14,6 +14,7 @@ using Content.Shared.Mobs.Components; using Content.Shared.Weapons.Melee.Events; using Content.Shared.Timing; using Robust.Shared.GameStates; +using Content.Shared.Forensics; namespace Content.Server.Chemistry.EntitySystems { @@ -138,6 +139,9 @@ namespace Content.Server.Chemistry.EntitySystems _reactiveSystem.DoEntityReaction(target.Value, removedSolution, ReactionMethod.Injection); _solutions.TryAddSolution(target.Value, targetSolution, removedSolution); + var ev = new TransferDnaEvent { Donor = target.Value, Recipient = uid }; + RaiseLocalEvent(target.Value, ref ev); + // same LogType as syringes... _adminLogger.Add(LogType.ForceFeed, $"{_entMan.ToPrettyString(user):user} injected {_entMan.ToPrettyString(target.Value):target} with a solution {SolutionContainerSystem.ToPrettyString(removedSolution):removedSolution} using a {_entMan.ToPrettyString(uid):using}"); diff --git a/Content.Server/Forensics/Components/ForensicScannerComponent.cs b/Content.Server/Forensics/Components/ForensicScannerComponent.cs index 16eb852590..ad26213848 100644 --- a/Content.Server/Forensics/Components/ForensicScannerComponent.cs +++ b/Content.Server/Forensics/Components/ForensicScannerComponent.cs @@ -13,21 +13,27 @@ namespace Content.Server.Forensics /// /// A list of fingerprint GUIDs that the forensic scanner found from the on an entity. /// - [ViewVariables(VVAccess.ReadOnly)] + [ViewVariables(VVAccess.ReadOnly), DataField("fingerprints")] public List Fingerprints = new(); /// /// A list of glove fibers that the forensic scanner found from the on an entity. /// - [ViewVariables(VVAccess.ReadOnly)] + [ViewVariables(VVAccess.ReadOnly), DataField("fibers")] public List Fibers = new(); /// /// DNA that the forensic scanner found from the on an entity. /// - [ViewVariables(VVAccess.ReadOnly)] + [ViewVariables(VVAccess.ReadOnly), DataField("dnas")] public List DNAs = new(); + /// + /// Residue that the forensic scanner found from the on an entity. + /// + [ViewVariables(VVAccess.ReadOnly), DataField("residues")] + public List Residues = new(); + /// /// What is the name of the entity that was scanned last? /// diff --git a/Content.Server/Forensics/Components/ForensicsComponent.cs b/Content.Server/Forensics/Components/ForensicsComponent.cs index 5a73a6a3e3..27eccf3334 100644 --- a/Content.Server/Forensics/Components/ForensicsComponent.cs +++ b/Content.Server/Forensics/Components/ForensicsComponent.cs @@ -11,5 +11,27 @@ namespace Content.Server.Forensics [DataField("dnas")] public HashSet DNAs = new(); + + [DataField("residues")] + public HashSet Residues = new(); + + /// + /// How long it takes to wipe the prints/blood/etc. off of this entity + /// + [DataField("cleanDelay")] + public float CleanDelay = 12.0f; + + /// + /// How close you must be to wipe the prints/blood/etc. off of this entity + /// + [DataField("cleanDistance")] + public float CleanDistance = 1.5f; + + /// + /// Can the DNA be cleaned off of this entity? + /// e.g. you can clean the DNA off of a knife, but not a puddle + /// + [DataField("canDnaBeCleaned")] + public bool CanDnaBeCleaned = true; } } diff --git a/Content.Server/Forensics/Components/ResidueComponent.cs b/Content.Server/Forensics/Components/ResidueComponent.cs new file mode 100644 index 0000000000..10895e7c2b --- /dev/null +++ b/Content.Server/Forensics/Components/ResidueComponent.cs @@ -0,0 +1,15 @@ +namespace Content.Server.Forensics; + +/// +/// This controls residues left on items +/// which the forensics system uses. +/// +[RegisterComponent] +public sealed partial class ResidueComponent : Component +{ + [DataField] + public LocId ResidueAdjective = "residue-unknown"; + + [DataField] + public string? ResidueColor; +} diff --git a/Content.Server/Forensics/Systems/ForensicScannerSystem.cs b/Content.Server/Forensics/Systems/ForensicScannerSystem.cs index a073574e1d..6710864c72 100644 --- a/Content.Server/Forensics/Systems/ForensicScannerSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicScannerSystem.cs @@ -51,6 +51,7 @@ namespace Content.Server.Forensics component.Fingerprints, component.Fibers, component.DNAs, + component.Residues, component.LastScannedName, component.PrintCooldown, component.PrintReadyAt); @@ -74,6 +75,7 @@ namespace Content.Server.Forensics scanner.Fingerprints = new(); scanner.Fibers = new(); scanner.DNAs = new(); + scanner.Residues = new(); } else @@ -81,6 +83,7 @@ namespace Content.Server.Forensics scanner.Fingerprints = forensics.Fingerprints.ToList(); scanner.Fibers = forensics.Fibers.ToList(); scanner.DNAs = forensics.DNAs.ToList(); + scanner.Residues = forensics.Residues.ToList(); } scanner.LastScannedName = MetaData(args.Args.Target.Value).EntityName; @@ -222,6 +225,12 @@ namespace Content.Server.Forensics { text.AppendLine(dna); } + text.AppendLine(); + text.AppendLine(Loc.GetString("forensic-scanner-interface-residues")); + foreach (var residue in component.Residues) + { + text.AppendLine(residue); + } _paperSystem.SetContent(printed, text.ToString()); _audioSystem.PlayPvs(component.SoundPrint, uid, diff --git a/Content.Server/Forensics/Systems/ForensicsSystem.cs b/Content.Server/Forensics/Systems/ForensicsSystem.cs index d4281c8b8d..3c6d1d30af 100644 --- a/Content.Server/Forensics/Systems/ForensicsSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicsSystem.cs @@ -1,5 +1,13 @@ +using Content.Server.Body.Components; +using Content.Server.Chemistry.EntitySystems; +using Content.Server.DoAfter; +using Content.Shared.DoAfter; +using Content.Shared.Forensics; +using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Inventory; +using Content.Shared.Tag; +using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Random; namespace Content.Server.Forensics @@ -8,11 +16,19 @@ namespace Content.Server.Forensics { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly TagSystem _tagSystem = default!; + [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; public override void Initialize() { SubscribeLocalEvent(OnInteract); SubscribeLocalEvent(OnFingerprintInit); SubscribeLocalEvent(OnDNAInit); + + SubscribeLocalEvent(OnBeingGibbed); + SubscribeLocalEvent(OnMeleeHit); + SubscribeLocalEvent(OnAfterInteract); + SubscribeLocalEvent(OnCleanForensicsDoAfter); + SubscribeLocalEvent(OnTransferDnaEvent); } private void OnInteract(EntityUid uid, FingerprintComponent component, ContactInteractionEvent args) @@ -30,6 +46,79 @@ namespace Content.Server.Forensics component.DNA = GenerateDNA(); } + private void OnBeingGibbed(EntityUid uid, DnaComponent component, BeingGibbedEvent args) + { + foreach(EntityUid part in args.GibbedParts) + { + var partComp = EnsureComp(part); + partComp.DNAs.Add(component.DNA); + partComp.CanDnaBeCleaned = false; + } + } + + private void OnMeleeHit(EntityUid uid, ForensicsComponent component, MeleeHitEvent args) + { + if((args.BaseDamage.DamageDict.TryGetValue("Blunt", out var bluntDamage) && bluntDamage.Value > 0) || + (args.BaseDamage.DamageDict.TryGetValue("Slash", out var slashDamage) && slashDamage.Value > 0) || + (args.BaseDamage.DamageDict.TryGetValue("Piercing", out var pierceDamage) && pierceDamage.Value > 0)) + { + foreach(EntityUid hitEntity in args.HitEntities) + { + if(TryComp(hitEntity, out var hitEntityComp)) + component.DNAs.Add(hitEntityComp.DNA); + } + } + } + + private void OnAfterInteract(EntityUid uid, ForensicsComponent component, AfterInteractEvent args) + { + if (args.Handled) + return; + + if (!_tagSystem.HasTag(args.Used, "CleansForensics")) + return; + + if((component.DNAs.Count > 0 && component.CanDnaBeCleaned) || (component.Fingerprints.Count + component.Fibers.Count > 0)) + { + var doAfterArgs = new DoAfterArgs(EntityManager, args.User, component.CleanDelay, new CleanForensicsDoAfterEvent(), uid, target: args.Target, used: args.Used) + { + BreakOnHandChange = true, + NeedHand = true, + BreakOnDamage = true, + BreakOnTargetMove = true, + MovementThreshold = 0.01f, + DistanceThreshold = component.CleanDistance, + }; + + + _doAfterSystem.TryStartDoAfter(doAfterArgs); + + args.Handled = true; + } + } + + private void OnCleanForensicsDoAfter(EntityUid uid, ForensicsComponent component, CleanForensicsDoAfterEvent args) + { + if (args.Handled || args.Cancelled || args.Args.Target == null) + return; + + if (!TryComp(args.Target, out var targetComp)) + return; + + targetComp.Fibers = new(); + targetComp.Fingerprints = new(); + + if (targetComp.CanDnaBeCleaned) + targetComp.DNAs = new(); + + // leave behind evidence it was cleaned + if (TryComp(args.Used, out var fiber)) + targetComp.Fibers.Add(string.IsNullOrEmpty(fiber.FiberColor) ? Loc.GetString("forensic-fibers", ("material", fiber.FiberMaterial)) : Loc.GetString("forensic-fibers-colored", ("color", fiber.FiberColor), ("material", fiber.FiberMaterial))); + + if (TryComp(args.Used, out var residue)) + targetComp.Residues.Add(string.IsNullOrEmpty(residue.ResidueColor) ? Loc.GetString("forensic-residue", ("adjective", residue.ResidueAdjective)) : Loc.GetString("forensic-residue-colored", ("color", residue.ResidueColor), ("adjective", residue.ResidueAdjective))); + } + public string GenerateFingerprint() { var fingerprint = new byte[16]; @@ -64,5 +153,31 @@ namespace Content.Server.Forensics if (TryComp(user, out var fingerprint)) component.Fingerprints.Add(fingerprint.Fingerprint ?? ""); } + + private void OnTransferDnaEvent(EntityUid uid, DnaComponent component, ref TransferDnaEvent args) + { + var recipientComp = EnsureComp(args.Recipient); + recipientComp.DNAs.Add(component.DNA); + recipientComp.CanDnaBeCleaned = args.CanDnaBeCleaned; + } + + #region Public API + + /// + /// Transfer DNA from one entity onto the forensics of another + /// + /// The entity receiving the DNA + /// The entity applying its DNA + /// If this DNA be cleaned off of the recipient. e.g. cleaning a knife vs cleaning a puddle of blood + public void TransferDna(EntityUid recipient, EntityUid donor, bool canDnaBeCleaned = true) + { + if (TryComp(donor, out var donorComp)) + { + EnsureComp(recipient, out var recipientComp); + recipientComp.DNAs.Add(donorComp.DNA); + } + } + + #endregion } } diff --git a/Content.Server/Medical/VomitSystem.cs b/Content.Server/Medical/VomitSystem.cs index 974c1981b5..fedb6d2357 100644 --- a/Content.Server/Medical/VomitSystem.cs +++ b/Content.Server/Medical/VomitSystem.cs @@ -28,6 +28,7 @@ namespace Content.Server.Medical [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; [Dependency] private readonly StunSystem _stun = default!; [Dependency] private readonly ThirstSystem _thirst = default!; + [Dependency] private readonly ForensicsSystem _forensics = default!; /// /// Make an entity vomit, if they have a stomach. @@ -85,9 +86,7 @@ namespace Content.Server.Medical if (_puddle.TrySpillAt(uid, solution, out var puddle, false)) { - var forensics = EnsureComp(puddle); - if (TryComp(uid, out var dna)) - forensics.DNAs.Add(dna.DNA); + _forensics.TransferDna(puddle, uid, false); } // Force sound to play as spill doesn't work if solution is empty. diff --git a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs index 1829dc32c8..e45c904591 100644 --- a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs @@ -54,6 +54,7 @@ public sealed class DrinkSystem : EntitySystem [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; [Dependency] private readonly StomachSystem _stomach = default!; + [Dependency] private readonly ForensicsSystem _forensics = default!; public override void Initialize() { @@ -399,9 +400,7 @@ public sealed class DrinkSystem : EntitySystem //TODO: Grab the stomach UIDs somehow without using Owner _stomach.TryTransferSolution(firstStomach.Value.Comp.Owner, drained, firstStomach.Value.Comp); - var comp = EnsureComp(uid); - if (TryComp(args.Target, out var dna)) - comp.DNAs.Add(dna.DNA); + _forensics.TransferDna(uid, args.Target.Value); if (!forceDrink && solution.Volume > 0) args.Repeat = true; diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs index 276393b728..63a558238b 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs @@ -15,6 +15,8 @@ using Content.Shared.Temperature; using Robust.Server.GameObjects; using Robust.Shared.Containers; using System.Linq; +using Content.Shared.Inventory.Events; +using Content.Server.Forensics; namespace Content.Server.Nutrition.EntitySystems { @@ -30,6 +32,7 @@ namespace Content.Server.Nutrition.EntitySystems [Dependency] private readonly SharedItemSystem _items = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly ForensicsSystem _forensics = default!; private const float UpdateTimer = 3f; @@ -44,6 +47,7 @@ namespace Content.Server.Nutrition.EntitySystems { SubscribeLocalEvent(OnSmokableIsHotEvent); SubscribeLocalEvent(OnSmokableShutdownEvent); + SubscribeLocalEvent(OnSmokeableEquipEvent); InitializeCigars(); InitializePipes(); @@ -85,6 +89,14 @@ namespace Content.Server.Nutrition.EntitySystems _active.Remove(uid); } + private void OnSmokeableEquipEvent(EntityUid uid, SmokableComponent component, GotEquippedEvent args) + { + if (args.Slot == "mask") + { + _forensics.TransferDna(uid, args.Equipee, false); + } + } + public override void Update(float frameTime) { _timer += frameTime; diff --git a/Content.Shared/Forensics/Events.cs b/Content.Shared/Forensics/Events.cs index d49bb24fbb..7300b78d76 100644 --- a/Content.Shared/Forensics/Events.cs +++ b/Content.Shared/Forensics/Events.cs @@ -24,3 +24,30 @@ public sealed partial class ForensicPadDoAfterEvent : DoAfterEvent public override DoAfterEvent Clone() => this; } + +[Serializable, NetSerializable] +public sealed partial class CleanForensicsDoAfterEvent : SimpleDoAfterEvent +{ +} + +/// +/// An event to apply DNA evidence from a donor onto some recipient. +/// +[ByRefEvent] +public record struct TransferDnaEvent() +{ + /// + /// The entity donating the DNA. + /// + public EntityUid Donor; + + /// + /// The entity receiving the DNA. + /// + public EntityUid Recipient; + + /// + /// Can the DNA be cleaned off? + /// + public bool CanDnaBeCleaned = true; +} diff --git a/Content.Shared/Forensics/ForensicScannerEvent.cs b/Content.Shared/Forensics/ForensicScannerEvent.cs index f305125b99..ce84b1f7b3 100644 --- a/Content.Shared/Forensics/ForensicScannerEvent.cs +++ b/Content.Shared/Forensics/ForensicScannerEvent.cs @@ -8,6 +8,7 @@ namespace Content.Shared.Forensics public readonly List Fingerprints = new(); public readonly List Fibers = new(); public readonly List DNAs = new(); + public readonly List Residues = new(); public readonly string LastScannedName = string.Empty; public readonly TimeSpan PrintCooldown = TimeSpan.Zero; public readonly TimeSpan PrintReadyAt = TimeSpan.Zero; @@ -16,6 +17,7 @@ namespace Content.Shared.Forensics List fingerprints, List fibers, List dnas, + List residues, string lastScannedName, TimeSpan printCooldown, TimeSpan printReadyAt) @@ -23,6 +25,7 @@ namespace Content.Shared.Forensics Fingerprints = fingerprints; Fibers = fibers; DNAs = dnas; + Residues = residues; LastScannedName = lastScannedName; PrintCooldown = printCooldown; PrintReadyAt = printReadyAt; diff --git a/Content.Shared/Implants/SharedImplanterSystem.cs b/Content.Shared/Implants/SharedImplanterSystem.cs index b3b2421f20..6c8284b9be 100644 --- a/Content.Shared/Implants/SharedImplanterSystem.cs +++ b/Content.Shared/Implants/SharedImplanterSystem.cs @@ -3,6 +3,7 @@ using System.Linq; using Content.Shared.Containers.ItemSlots; using Content.Shared.DoAfter; using Content.Shared.Examine; +using Content.Shared.Forensics; using Content.Shared.IdentityManagement; using Content.Shared.Implants.Components; using Content.Shared.Popups; @@ -72,6 +73,9 @@ public abstract class SharedImplanterSystem : EntitySystem else ImplantMode(implanter, component); + var ev = new TransferDnaEvent { Donor = target, Recipient = implanter }; + RaiseLocalEvent(target, ref ev); + Dirty(component); } @@ -140,6 +144,10 @@ public abstract class SharedImplanterSystem : EntitySystem implantComp.ImplantedEntity = null; implanterContainer.Insert(implant); permanentFound = implantComp.Permanent; + + var ev = new TransferDnaEvent { Donor = target, Recipient = implanter }; + RaiseLocalEvent(target, ref ev); + //Break so only one implant is drawn break; } diff --git a/Resources/Locale/en-US/forensics/forensics.ftl b/Resources/Locale/en-US/forensics/forensics.ftl index 88494820ec..2326aeb9c2 100644 --- a/Resources/Locale/en-US/forensics/forensics.ftl +++ b/Resources/Locale/en-US/forensics/forensics.ftl @@ -2,6 +2,7 @@ forensic-scanner-interface-title = Forensic scanner forensic-scanner-interface-fingerprints = Fingerprints forensic-scanner-interface-fibers = Fibers forensic-scanner-interface-dnas = DNAs +forensic-scanner-interface-residues = Residues forensic-scanner-interface-no-data = No scan data available forensic-scanner-interface-print = Print forensic-scanner-interface-clear = Clear diff --git a/Resources/Locale/en-US/forensics/residues.ftl b/Resources/Locale/en-US/forensics/residues.ftl new file mode 100644 index 0000000000..3c5fe3c96d --- /dev/null +++ b/Resources/Locale/en-US/forensics/residues.ftl @@ -0,0 +1,11 @@ +forensic-residue = {LOC($adjective)} residue +forensic-residue-colored = {LOC($adjective)} {LOC($color)} residue + +residue-unknown = unknown +residue-slippery = slippery + +residue-green = green +residue-blue = blue +residue-red = red +residue-grey = grey +residue-brown = brown \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml index c7deba0732..4119b12a46 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml @@ -611,3 +611,6 @@ tags: - DroneUsable - Mop + - CleansForensics + - type: Fiber + fiberColor: fibers-white diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/soap.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/soap.yml index ca267a308c..5905c6b128 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/soap.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/soap.yml @@ -7,6 +7,7 @@ - type: Tag tags: - Soap + - CleansForensics - type: Sprite sprite: Objects/Specific/Janitorial/soap.rsi layers: @@ -68,6 +69,9 @@ - type: Food solution: soap - type: BadFood + - type: Residue + residueAdjective: residue-slippery + residueColor: residue-green - type: entity name: soap @@ -90,6 +94,9 @@ reagents: - ReagentId: SoapReagent Quantity: 100 + - type: Residue + residueAdjective: residue-slippery + residueColor: residue-grey - type: entity name: soap @@ -105,6 +112,9 @@ fillBaseName: deluxe- - type: Item heldPrefix: deluxe + - type: Residue + residueAdjective: residue-slippery + residueColor: residue-brown - type: entity name: soap @@ -127,6 +137,9 @@ flavors: - clean - punishment + - type: Residue + residueAdjective: residue-slippery + residueColor: residue-red - type: entity name: soaplet @@ -189,6 +202,9 @@ flavors: - clean - meaty + - type: Residue + residueAdjective: residue-slippery + residueColor: residue-red - type: entity name: omega soap @@ -214,3 +230,6 @@ reagents: - ReagentId: SoapReagent Quantity: 240 + - type: Residue + residueAdjective: residue-slippery + residueColor: residue-blue \ No newline at end of file diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 7793d47d37..dfdaa85789 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -1124,3 +1124,6 @@ - type: Tag id: boots + +- type: Tag + id: CleansForensics \ No newline at end of file -- 2.51.2