mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-17 13:37:47 +03:00
# Description Cherry-picked from Delta-V, originally by @WarMechanic (https://github.com/DeltaV-Station/Delta-v/pull/1455). Original Description: > Every pair of gloves now has its own fingerprint, so items can be traced back to gloves which can then be traced back to people. > > ## Why / Balance > > Evidence is very important to court cases running smoothly, so detectives now have more evidence that can certify whether or not John Syndicate's behaviour is valid. Traitors are now encouraged to either clean evidence off syndicate gear regardless of glove status, or use a disposable pair of gloves specifically for handling syndicate gear to be disposed of later. > > Aside from being required to obfuscate evidence you leave behind, there is now a value proposition to searching glove prints of departments. Wearing gloves that does not correspond your department can punish an unknowing detective into searching the wrong people. > > ## Technical details > > `FiberComponent.cs` now stores a Fiberprint variable like `FingerprintComponent.cs`. The code for assigning a fiberprint is the same as the fingerprint. When evidence is placed on an object, the fiberprint is concatenated to its localised fiber type. <details><summary><h1>Original Media</h1></summary> <p> > hm ok we have these specific gloves on an akms > <br> > > <img src="https://private-user-images.githubusercontent.com/69510347/345294367-55819ed3-1e89-401f-b7fb-0d8569bd7aa2.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjI1NjU2ODYsIm5iZiI6MTcyMjU2NTM4NiwicGF0aCI6Ii82OTUxMDM0Ny8zNDUyOTQzNjctNTU4MTllZDMtMWU4OS00MDFmLWI3ZmItMGQ4NTY5YmQ3YWEyLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MDIlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODAyVDAyMjMwNlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTYxZWUxMjZjY2FiMTU1ODdlOGFiNDliN2ZjZjg4MmYxZmY3ZWY0MGMzN2UxZWM1MjA2MjBlYjY1ZDQ2YTJjMGYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.t5sASM_0is4bqd1YIizQ0lldAZ5RAStbNzXNuzHmdfU"> > > hm well we found the gloves and they have fingerprints > <br> > > <img src="https://private-user-images.githubusercontent.com/69510347/345294443-a9b21171-bee5-4c5f-8ec6-cf5576572d45.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjI1NjU2ODYsIm5iZiI6MTcyMjU2NTM4NiwicGF0aCI6Ii82OTUxMDM0Ny8zNDUyOTQ0NDMtYTliMjExNzEtYmVlNS00YzVmLThlYzYtY2Y1NTc2NTcyZDQ1LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MDIlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODAyVDAyMjMwNlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTM3ZTY3NjRhZmEzMjRhYTkwNjUyOWI3MzgzOGM0ZWRjY2Q3MTg0YzViN2NjMDQyZTgzMDBiMmZlZDAyYWJjYjUmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.uSn27Drf1K8rcfQtYRo_fGKudbO03fp40GNHUSd4Uw0"> > > gotem > <br> > > <img src="https://private-user-images.githubusercontent.com/69510347/345294064-d6254f48-2d81-4702-ac03-c1c796712ef4.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjI1NjU2ODYsIm5iZiI6MTcyMjU2NTM4NiwicGF0aCI6Ii82OTUxMDM0Ny8zNDUyOTQwNjQtZDYyNTRmNDgtMmQ4MS00NzAyLWFjMDMtYzFjNzk2NzEyZWY0LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MDIlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODAyVDAyMjMwNlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTgzNWYzODBmOWVhYzQzMjMxNDdlMzk5Zjk2ODY4N2I1ZWNmYmNkNGI2NGFlMjZiMDM1NGVjNDJlOGFjNjViN2YmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.TjsSLQpLoGsXOHS-wl_LSASdLxgeVTKxCGugkX7-v90"> </details> </p> # Changelog 🆑 WarMechanic - add: Gloves now have unique fingerprints. Items can be traced back to gloves, which can then be traced back to people. --------- Signed-off-by: Angelo Fallaria <ba.fallaria@gmail.com> Co-authored-by: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com> Co-authored-by: Danger Revolution! <142105406+DangerRevolution@users.noreply.github.com> Co-authored-by: VMSolidus <evilexecutive@gmail.com>
230 lines
9.6 KiB
C#
230 lines
9.6 KiB
C#
using Content.Server.Body.Components;
|
|
using Content.Server.DoAfter;
|
|
using Content.Server.Fluids.EntitySystems;
|
|
using Content.Server.Forensics.Components;
|
|
using Content.Server.Popups;
|
|
using Content.Shared.Chemistry.Components;
|
|
using Content.Shared.DoAfter;
|
|
using Content.Shared.Forensics;
|
|
using Content.Shared.Interaction;
|
|
using Content.Shared.Interaction.Events;
|
|
using Content.Shared.Inventory;
|
|
using Content.Shared.Weapons.Melee.Events;
|
|
using Robust.Shared.Random;
|
|
|
|
namespace Content.Server.Forensics
|
|
{
|
|
public sealed class ForensicsSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly IRobustRandom _random = default!;
|
|
[Dependency] private readonly InventorySystem _inventory = default!;
|
|
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
|
public override void Initialize()
|
|
{
|
|
SubscribeLocalEvent<FingerprintComponent, ContactInteractionEvent>(OnInteract);
|
|
SubscribeLocalEvent<FiberComponent, MapInitEvent>(OnFiberInit);
|
|
SubscribeLocalEvent<FingerprintComponent, MapInitEvent>(OnFingerprintInit);
|
|
SubscribeLocalEvent<DnaComponent, MapInitEvent>(OnDNAInit);
|
|
|
|
SubscribeLocalEvent<DnaComponent, BeingGibbedEvent>(OnBeingGibbed);
|
|
SubscribeLocalEvent<ForensicsComponent, MeleeHitEvent>(OnMeleeHit);
|
|
SubscribeLocalEvent<ForensicsComponent, GotRehydratedEvent>(OnRehydrated);
|
|
SubscribeLocalEvent<CleansForensicsComponent, AfterInteractEvent>(OnAfterInteract, after: new[] { typeof(AbsorbentSystem) });
|
|
SubscribeLocalEvent<ForensicsComponent, CleanForensicsDoAfterEvent>(OnCleanForensicsDoAfter);
|
|
SubscribeLocalEvent<DnaComponent, TransferDnaEvent>(OnTransferDnaEvent);
|
|
}
|
|
|
|
private void OnInteract(EntityUid uid, FingerprintComponent component, ContactInteractionEvent args)
|
|
{
|
|
ApplyEvidence(uid, args.Other);
|
|
}
|
|
|
|
private void OnFiberInit(EntityUid uid, FiberComponent component, MapInitEvent args)
|
|
{
|
|
component.Fiberprint = GenerateFingerprint(length: 7);
|
|
}
|
|
|
|
private void OnFingerprintInit(EntityUid uid, FingerprintComponent component, MapInitEvent args)
|
|
{
|
|
component.Fingerprint = GenerateFingerprint();
|
|
}
|
|
|
|
private void OnDNAInit(EntityUid uid, DnaComponent component, MapInitEvent args)
|
|
{
|
|
component.DNA = GenerateDNA();
|
|
}
|
|
|
|
private void OnBeingGibbed(EntityUid uid, DnaComponent component, BeingGibbedEvent args)
|
|
{
|
|
foreach(EntityUid part in args.GibbedParts)
|
|
{
|
|
var partComp = EnsureComp<ForensicsComponent>(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<DnaComponent>(hitEntity, out var hitEntityComp))
|
|
component.DNAs.Add(hitEntityComp.DNA);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnRehydrated(Entity<ForensicsComponent> ent, ref GotRehydratedEvent args)
|
|
{
|
|
CopyForensicsFrom(ent.Comp, args.Target);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copy forensic information from a source entity to a destination.
|
|
/// Existing forensic information on the target is still kept.
|
|
/// </summary>
|
|
public void CopyForensicsFrom(ForensicsComponent src, EntityUid target)
|
|
{
|
|
var dest = EnsureComp<ForensicsComponent>(target);
|
|
foreach (var dna in src.DNAs)
|
|
{
|
|
dest.DNAs.Add(dna);
|
|
}
|
|
|
|
foreach (var fiber in src.Fibers)
|
|
{
|
|
dest.Fibers.Add(fiber);
|
|
}
|
|
|
|
foreach (var print in src.Fingerprints)
|
|
{
|
|
dest.Fingerprints.Add(print);
|
|
}
|
|
}
|
|
|
|
private void OnAfterInteract(EntityUid uid, CleansForensicsComponent component, AfterInteractEvent args)
|
|
{
|
|
if (args.Handled)
|
|
return;
|
|
|
|
if (!TryComp<ForensicsComponent>(args.Target, out var forensicsComp))
|
|
return;
|
|
|
|
if((forensicsComp.DNAs.Count > 0 && forensicsComp.CanDnaBeCleaned) || (forensicsComp.Fingerprints.Count + forensicsComp.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 = forensicsComp.CleanDistance,
|
|
};
|
|
|
|
_doAfterSystem.TryStartDoAfter(doAfterArgs);
|
|
_popupSystem.PopupEntity(Loc.GetString("forensics-cleaning", ("target", args.Target)), args.User, args.User);
|
|
|
|
args.Handled = true;
|
|
}
|
|
}
|
|
|
|
private void OnCleanForensicsDoAfter(EntityUid uid, ForensicsComponent component, CleanForensicsDoAfterEvent args)
|
|
{
|
|
if (args.Handled || args.Cancelled || args.Args.Target == null)
|
|
return;
|
|
|
|
if (!TryComp<ForensicsComponent>(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<FiberComponent>(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<ResidueComponent>(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(int length = 16)
|
|
{
|
|
var fingerprint = new byte[Math.Clamp(length, 0, 255)];
|
|
_random.NextBytes(fingerprint);
|
|
return Convert.ToHexString(fingerprint);
|
|
}
|
|
|
|
public string GenerateDNA()
|
|
{
|
|
var letters = new[] { "A", "C", "G", "T" };
|
|
var DNA = string.Empty;
|
|
|
|
for (var i = 0; i < 16; i++)
|
|
{
|
|
DNA += letters[_random.Next(letters.Length)];
|
|
}
|
|
|
|
return DNA;
|
|
}
|
|
|
|
private void ApplyEvidence(EntityUid user, EntityUid target)
|
|
{
|
|
if (HasComp<IgnoresFingerprintsComponent>(target))
|
|
return;
|
|
|
|
var component = EnsureComp<ForensicsComponent>(target);
|
|
if (_inventory.TryGetSlotEntity(user, "gloves", out var gloves))
|
|
{
|
|
if (TryComp<FiberComponent>(gloves, out var fiber) && !string.IsNullOrEmpty(fiber.FiberMaterial))
|
|
{
|
|
var fiberLocale = string.IsNullOrEmpty(fiber.FiberColor)
|
|
? Loc.GetString("forensic-fibers", ("material", fiber.FiberMaterial))
|
|
: Loc.GetString("forensic-fibers-colored", ("color", fiber.FiberColor), ("material", fiber.FiberMaterial));
|
|
component.Fibers.Add(fiberLocale + " ; " + fiber.Fiberprint);
|
|
}
|
|
|
|
if (HasComp<FingerprintMaskComponent>(gloves))
|
|
return;
|
|
}
|
|
if (TryComp<FingerprintComponent>(user, out var fingerprint))
|
|
component.Fingerprints.Add(fingerprint.Fingerprint ?? "");
|
|
}
|
|
|
|
private void OnTransferDnaEvent(EntityUid uid, DnaComponent component, ref TransferDnaEvent args)
|
|
{
|
|
var recipientComp = EnsureComp<ForensicsComponent>(args.Recipient);
|
|
recipientComp.DNAs.Add(component.DNA);
|
|
recipientComp.CanDnaBeCleaned = args.CanDnaBeCleaned;
|
|
}
|
|
|
|
#region Public API
|
|
|
|
/// <summary>
|
|
/// Transfer DNA from one entity onto the forensics of another
|
|
/// </summary>
|
|
/// <param name="recipient">The entity receiving the DNA</param>
|
|
/// <param name="donor">The entity applying its DNA</param>
|
|
/// <param name="canDnaBeCleaned">If this DNA be cleaned off of the recipient. e.g. cleaning a knife vs cleaning a puddle of blood</param>
|
|
public void TransferDna(EntityUid recipient, EntityUid donor, bool canDnaBeCleaned = true)
|
|
{
|
|
if (TryComp<DnaComponent>(donor, out var donorComp))
|
|
{
|
|
EnsureComp<ForensicsComponent>(recipient, out var recipientComp);
|
|
recipientComp.DNAs.Add(donorComp.DNA);
|
|
recipientComp.CanDnaBeCleaned = canDnaBeCleaned;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|