DNA basics (#14724)
* DNA component * Commit numba 2 * Added DNA into Station Records Computer * commit numba 3 * commit numba 4 * Vomit also contain DNA component now * fixed DNA field not clearing after scanning another item * commit numba 10 Drinking leaves DNA on an object. Breaking glasses, bottles and beakers leave DNA and leave fingerprints/fibers with 40% chance on glass shards. + lotta fixes * 11 * 12 * 14 * Added DNA guide entry * FIX
This commit is contained in:
@@ -52,6 +52,12 @@ namespace Content.Client.Forensics
|
||||
{
|
||||
text.AppendLine(fiber);
|
||||
}
|
||||
text.AppendLine();
|
||||
text.AppendLine(Loc.GetString("forensic-scanner-interface-dnas"));
|
||||
foreach (var dna in msg.DNAs)
|
||||
{
|
||||
text.AppendLine(dna);
|
||||
}
|
||||
Diagnostics.Text = text.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,11 @@ public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
||||
},
|
||||
new Label()
|
||||
{
|
||||
Text = Loc.GetString("general-station-record-console-record-fingerprint", ("fingerprint", record.Fingerprint is null ? Loc.GetString("generic-not-available-shorthand") : record.Fingerprint))
|
||||
Text = Loc.GetString("general-station-record-console-record-fingerprint", ("fingerprint", record.Fingerprint ?? Loc.GetString("generic-not-available-shorthand")))
|
||||
},
|
||||
new Label()
|
||||
{
|
||||
Text = Loc.GetString("general-station-record-console-record-dna", ("dna", record.DNA ?? Loc.GetString("generic-not-available-shorthand")))
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using Content.Server.Body.Components;
|
||||
using Content.Server.Chemistry.EntitySystems;
|
||||
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.Chemistry.Components;
|
||||
@@ -292,7 +293,14 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
// Pass some of the chemstream into the spilled blood.
|
||||
var temp = component.ChemicalSolution.SplitSolution(component.BloodTemporarySolution.Volume / 10);
|
||||
component.BloodTemporarySolution.AddSolution(temp, _prototypeManager);
|
||||
_spillableSystem.SpillAt(uid, component.BloodTemporarySolution, "PuddleBlood", false);
|
||||
var puddle = _spillableSystem.SpillAt(uid, component.BloodTemporarySolution, "PuddleBlood", false);
|
||||
if (puddle != null)
|
||||
{
|
||||
var comp = EnsureComp<ForensicsComponent>(puddle.Owner); //TODO: Get rid of .Owner
|
||||
if (TryComp<DnaComponent>(uid, out var dna))
|
||||
comp.DNAs.Add(dna.DNA);
|
||||
}
|
||||
|
||||
component.BloodTemporarySolution.RemoveAllSolution();
|
||||
}
|
||||
|
||||
@@ -331,6 +339,13 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
component.BloodTemporarySolution.RemoveAllSolution();
|
||||
tempSol.AddSolution(component.ChemicalSolution, _prototypeManager);
|
||||
component.ChemicalSolution.RemoveAllSolution();
|
||||
_spillableSystem.SpillAt(uid, tempSol, "PuddleBlood", true);
|
||||
var puddle = _spillableSystem.SpillAt(uid, tempSol, "PuddleBlood", true);
|
||||
|
||||
if (puddle != null)
|
||||
{
|
||||
var comp = EnsureComp<ForensicsComponent>(puddle.Owner); //TODO: Get rid of .Owner
|
||||
if (TryComp<DnaComponent>(uid, out var dna))
|
||||
comp.DNAs.Add(dna.DNA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using Content.Server.Forensics;
|
||||
using Content.Server.Stack;
|
||||
using Content.Shared.Prototypes;
|
||||
using Content.Shared.Stacks;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds.Behaviors
|
||||
@@ -19,6 +21,9 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
|
||||
[DataField("offset")]
|
||||
public float Offset { get; set; } = 0.5f;
|
||||
|
||||
[DataField("transferForensics")]
|
||||
public bool DoTransferForensics = false;
|
||||
|
||||
public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null)
|
||||
{
|
||||
var position = system.EntityManager.GetComponent<TransformComponent>(owner).MapPosition;
|
||||
@@ -37,15 +42,34 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
|
||||
{
|
||||
var spawned = system.EntityManager.SpawnEntity(entityId, position.Offset(getRandomVector()));
|
||||
system.StackSystem.SetCount(spawned, count);
|
||||
|
||||
TransferForensics(spawned, system, owner);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
system.EntityManager.SpawnEntity(entityId, position.Offset(getRandomVector()));
|
||||
var spawned = system.EntityManager.SpawnEntity(entityId, position.Offset(getRandomVector()));
|
||||
|
||||
TransferForensics(spawned, system, owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void TransferForensics(EntityUid spawned, DestructibleSystem system, EntityUid owner)
|
||||
{
|
||||
if (!DoTransferForensics ||
|
||||
!system.EntityManager.TryGetComponent<ForensicsComponent>(owner, out var forensicsComponent))
|
||||
return;
|
||||
|
||||
var comp = system.EntityManager.EnsureComponent<ForensicsComponent>(spawned);
|
||||
comp.DNAs = forensicsComponent.DNAs;
|
||||
|
||||
if (!system.Random.Prob(0.4f))
|
||||
return;
|
||||
comp.Fingerprints = forensicsComponent.Fingerprints;
|
||||
comp.Fibers = forensicsComponent.Fibers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
Content.Server/Forensics/Components/DnaComponent.cs
Normal file
11
Content.Server/Forensics/Components/DnaComponent.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Content.Server.Forensics;
|
||||
|
||||
/// <summary>
|
||||
/// This component is for mobs that have DNA.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class DnaComponent : Component
|
||||
{
|
||||
[DataField("dna"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public string DNA = String.Empty;
|
||||
}
|
||||
@@ -20,6 +20,12 @@ namespace Content.Server.Forensics
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public List<string> Fibers = new();
|
||||
|
||||
/// <summary>
|
||||
/// DNA that the forensic scanner found from the <see cref="DNAComponent"/> on an entity.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public List<string> DNAs = new();
|
||||
|
||||
/// <summary>
|
||||
/// What is the name of the entity that was scanned last?
|
||||
/// </summary>
|
||||
|
||||
@@ -8,5 +8,8 @@ namespace Content.Server.Forensics
|
||||
|
||||
[DataField("fibers")]
|
||||
public HashSet<string> Fibers = new();
|
||||
|
||||
[DataField("dnas")]
|
||||
public HashSet<string> DNAs = new();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace Content.Server.Forensics
|
||||
var state = new ForensicScannerBoundUserInterfaceState(
|
||||
component.Fingerprints,
|
||||
component.Fibers,
|
||||
component.DNAs,
|
||||
component.LastScannedName,
|
||||
component.PrintCooldown,
|
||||
component.PrintReadyAt);
|
||||
@@ -69,12 +70,14 @@ namespace Content.Server.Forensics
|
||||
{
|
||||
scanner.Fingerprints = new();
|
||||
scanner.Fibers = new();
|
||||
scanner.DNAs = new();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
scanner.Fingerprints = forensics.Fingerprints.ToList();
|
||||
scanner.Fibers = forensics.Fibers.ToList();
|
||||
scanner.DNAs = forensics.DNAs.ToList();
|
||||
}
|
||||
|
||||
scanner.LastScannedName = MetaData(args.Args.Target.Value).EntityName;
|
||||
@@ -211,6 +214,12 @@ namespace Content.Server.Forensics
|
||||
{
|
||||
text.AppendLine(fiber);
|
||||
}
|
||||
text.AppendLine();
|
||||
text.AppendLine(Loc.GetString("forensic-scanner-interface-dnas"));
|
||||
foreach (var dna in component.DNAs)
|
||||
{
|
||||
text.AppendLine(dna);
|
||||
}
|
||||
|
||||
_paperSystem.SetContent(printed, text.ToString());
|
||||
_audioSystem.PlayPvs(component.SoundPrint, uid,
|
||||
@@ -230,6 +239,7 @@ namespace Content.Server.Forensics
|
||||
|
||||
component.Fingerprints = new();
|
||||
component.Fibers = new();
|
||||
component.DNAs = new();
|
||||
component.LastScannedName = string.Empty;
|
||||
|
||||
UpdateUserInterface(uid, component);
|
||||
|
||||
@@ -11,7 +11,8 @@ namespace Content.Server.Forensics
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<FingerprintComponent, ContactInteractionEvent>(OnInteract);
|
||||
SubscribeLocalEvent<FingerprintComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<FingerprintComponent, ComponentInit>(OnFingeprintInit);
|
||||
SubscribeLocalEvent<DnaComponent, ComponentInit>(OnDNAInit);
|
||||
}
|
||||
|
||||
private void OnInteract(EntityUid uid, FingerprintComponent component, ContactInteractionEvent args)
|
||||
@@ -19,11 +20,16 @@ namespace Content.Server.Forensics
|
||||
ApplyEvidence(uid, args.Other);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, FingerprintComponent component, ComponentInit args)
|
||||
private void OnFingeprintInit(EntityUid uid, FingerprintComponent component, ComponentInit args)
|
||||
{
|
||||
component.Fingerprint = GenerateFingerprint();
|
||||
}
|
||||
|
||||
private void OnDNAInit(EntityUid uid, DnaComponent component, ComponentInit args)
|
||||
{
|
||||
component.DNA = GenerateDNA();
|
||||
}
|
||||
|
||||
private string GenerateFingerprint()
|
||||
{
|
||||
byte[] fingerprint = new byte[16];
|
||||
@@ -31,6 +37,19 @@ namespace Content.Server.Forensics
|
||||
return Convert.ToHexString(fingerprint);
|
||||
}
|
||||
|
||||
private string GenerateDNA()
|
||||
{
|
||||
var letters = new List<string> { "A", "C", "G", "T" };
|
||||
string DNA = String.Empty;
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
DNA += letters[_random.Next(letters.Count)];
|
||||
}
|
||||
|
||||
return DNA;
|
||||
}
|
||||
|
||||
private void ApplyEvidence(EntityUid user, EntityUid target)
|
||||
{
|
||||
var component = EnsureComp<ForensicsComponent>(target);
|
||||
|
||||
@@ -2,6 +2,7 @@ using Content.Server.Body.Components;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Chemistry.EntitySystems;
|
||||
using Content.Server.Fluids.Components;
|
||||
using Content.Server.Forensics;
|
||||
using Content.Server.Nutrition.Components;
|
||||
using Content.Server.Nutrition.EntitySystems;
|
||||
using Content.Server.Popups;
|
||||
@@ -49,6 +50,10 @@ namespace Content.Server.Medical
|
||||
|
||||
var puddle = EntityManager.SpawnEntity("PuddleVomit", Transform(uid).Coordinates);
|
||||
|
||||
var forensics = EnsureComp<ForensicsComponent>(puddle);
|
||||
if (TryComp<DnaComponent>(uid, out var dna))
|
||||
forensics.DNAs.Add(dna.DNA);
|
||||
|
||||
var puddleComp = Comp<PuddleComponent>(puddle);
|
||||
|
||||
SoundSystem.Play("/Audio/Effects/Fluids/splat.ogg", Filter.Pvs(uid), uid, AudioHelpers.WithVariation(0.2f).WithVolume(-4f));
|
||||
|
||||
@@ -4,6 +4,7 @@ using Content.Server.Chemistry.Components.SolutionManager;
|
||||
using Content.Server.Chemistry.EntitySystems;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Server.Fluids.EntitySystems;
|
||||
using Content.Server.Forensics;
|
||||
using Content.Server.Nutrition.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Administration.Logs;
|
||||
@@ -377,6 +378,10 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
|
||||
component.ForceDrink = false;
|
||||
args.Handled = true;
|
||||
|
||||
var comp = EnsureComp<ForensicsComponent>(uid);
|
||||
if (TryComp<DnaComponent>(args.Args.Target, out var dna))
|
||||
comp.DNAs.Add(dna.DNA);
|
||||
}
|
||||
|
||||
private void AddDrinkVerb(EntityUid uid, DrinkComponent component, GetVerbsEvent<AlternativeVerb> ev)
|
||||
|
||||
@@ -74,8 +74,9 @@ public sealed class StationRecordsSystem : EntitySystem
|
||||
}
|
||||
|
||||
TryComp<FingerprintComponent>(player, out var fingerprintComponent);
|
||||
TryComp<DnaComponent>(player, out var dnaComponent);
|
||||
|
||||
CreateGeneralRecord(station, idUid.Value, profile.Name, profile.Age, profile.Species, profile.Gender, jobId, fingerprintComponent?.Fingerprint, profile, records);
|
||||
CreateGeneralRecord(station, idUid.Value, profile.Name, profile.Age, profile.Species, profile.Gender, jobId, fingerprintComponent?.Fingerprint, dnaComponent?.DNA, profile, records);
|
||||
}
|
||||
|
||||
|
||||
@@ -97,13 +98,16 @@ public sealed class StationRecordsSystem : EntitySystem
|
||||
/// 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="mobFingerprint">Fingerprint of the character.</param>
|
||||
/// <param name="dna">DNA of the character.</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, string? mobFingerprint, HumanoidCharacterProfile? profile = null,
|
||||
public void CreateGeneralRecord(EntityUid station, EntityUid? idUid, string name, int age, string species, Gender gender, string jobId, string? mobFingerprint, string? dna, HumanoidCharacterProfile? profile = null,
|
||||
StationRecordsComponent? records = null)
|
||||
{
|
||||
if (!Resolve(station, ref records))
|
||||
@@ -126,7 +130,8 @@ public sealed class StationRecordsSystem : EntitySystem
|
||||
Species = species,
|
||||
Gender = gender,
|
||||
DisplayPriority = jobPrototype.Weight,
|
||||
Fingerprint = mobFingerprint
|
||||
Fingerprint = mobFingerprint,
|
||||
DNA = dna
|
||||
};
|
||||
|
||||
var key = AddRecord(station, records);
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Content.Shared.Forensics
|
||||
{
|
||||
public readonly List<string> Fingerprints = new();
|
||||
public readonly List<string> Fibers = new();
|
||||
public readonly List<string> DNAs = new();
|
||||
public readonly string LastScannedName = string.Empty;
|
||||
public readonly TimeSpan PrintCooldown = TimeSpan.Zero;
|
||||
public readonly TimeSpan PrintReadyAt = TimeSpan.Zero;
|
||||
@@ -14,12 +15,14 @@ namespace Content.Shared.Forensics
|
||||
public ForensicScannerBoundUserInterfaceState(
|
||||
List<string> fingerprints,
|
||||
List<string> fibers,
|
||||
List<string> dnas,
|
||||
string lastScannedName,
|
||||
TimeSpan printCooldown,
|
||||
TimeSpan printReadyAt)
|
||||
{
|
||||
Fingerprints = fingerprints;
|
||||
Fibers = fibers;
|
||||
DNAs = dnas;
|
||||
LastScannedName = lastScannedName;
|
||||
PrintCooldown = printCooldown;
|
||||
PrintReadyAt = printReadyAt;
|
||||
|
||||
@@ -62,4 +62,10 @@ public sealed class GeneralStationRecord
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string? Fingerprint;
|
||||
|
||||
/// <summary>
|
||||
/// DNA of the person.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string? DNA;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,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-no-data = No scan data available
|
||||
forensic-scanner-interface-print = Print
|
||||
forensic-scanner-interface-clear = Clear
|
||||
|
||||
@@ -21,3 +21,6 @@ guide-entry-xenoarchaeology = Xenoarchaeology
|
||||
guide-entry-artifact-reports = Artifact Reports
|
||||
guide-entry-traversal-distorter = Traversal Distorter
|
||||
guide-entry-machine-upgrading = Machine Upgrading
|
||||
|
||||
guide-entry-security = Security
|
||||
guide-entry-dna = DNA
|
||||
|
||||
@@ -8,3 +8,4 @@ general-station-record-console-record-title = Job: {$job}
|
||||
general-station-record-console-record-species = Species: {$species}
|
||||
general-station-record-console-record-gender = Gender: {$gender}
|
||||
general-station-record-console-record-fingerprint = Fingerprint: {$fingerprint}
|
||||
general-station-record-console-record-dna = DNA: {$dna}
|
||||
|
||||
@@ -297,6 +297,7 @@
|
||||
proper: true
|
||||
- type: StandingState
|
||||
- type: Fingerprint
|
||||
- type: Dna
|
||||
- type: MobPrice
|
||||
price: 1500 # Kidnapping a living person and selling them for cred is a good move.
|
||||
deathPenalty: 0.01 # However they really ought to be living and intact, otherwise they're worth 100x less.
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
ShardGlass:
|
||||
min: 1
|
||||
max: 1
|
||||
transferForensics: true
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- type: DamageOnLand
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
BrokenBottle:
|
||||
min: 1
|
||||
max: 1
|
||||
transferForensics: true
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- type: Tag
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
ShardGlass:
|
||||
min: 1
|
||||
max: 1
|
||||
transferForensics: true
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- type: DamageOnLand
|
||||
|
||||
11
Resources/Prototypes/Guidebook/security.yml
Normal file
11
Resources/Prototypes/Guidebook/security.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
- type: guideEntry
|
||||
id: Security
|
||||
name: guide-entry-security
|
||||
text: "/Server Info/Guidebook/Security/Security.xml"
|
||||
children:
|
||||
- DNA
|
||||
|
||||
- type: guideEntry
|
||||
id: DNA
|
||||
name: guide-entry-dna
|
||||
text: "/Server Info/Guidebook/Security/DNA.xml"
|
||||
@@ -6,6 +6,7 @@
|
||||
- Botany
|
||||
- Engineering
|
||||
- Science
|
||||
- Security
|
||||
|
||||
- type: guideEntry
|
||||
id: Survival
|
||||
|
||||
18
Resources/Server Info/Guidebook/Security/DNA.xml
Normal file
18
Resources/Server Info/Guidebook/Security/DNA.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<Document>
|
||||
# DNA
|
||||
|
||||
## How to get someone’s DNA?
|
||||
|
||||
You can scan blood puddles, vomit, glasses, bottles, cans and other liquid containers using the [color=#a4885c]forensic scanner[/color] to get DNA of any person.
|
||||
<Box>
|
||||
<GuideEntityEmbed Entity="ForensicScanner"/>
|
||||
</Box>
|
||||
So be careful before fighting with someone.
|
||||
|
||||
## I got DNA. How do I recognize whose it is?
|
||||
|
||||
You can print the forensic information of the object you scanned so you never miss it. Now with the paper containing DNA you can simply find a [color=#a4885c]Station Records Computer[/color] and look for a person whose DNA matches.
|
||||
<Box>
|
||||
<GuideEntityEmbed Entity="ComputerStationRecords"/>
|
||||
</Box>
|
||||
</Document>
|
||||
3
Resources/Server Info/Guidebook/Security/Security.xml
Normal file
3
Resources/Server Info/Guidebook/Security/Security.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<Document>
|
||||
|
||||
</Document>
|
||||
Reference in New Issue
Block a user