Better DNA forensics & ReagentData (#26699)

* Added the ability for blood to track DNA using ReagentData; Forensic Scanner now accounts for solution DNA, non-DNA holders have "Unknown DNA"

* Removes touch DNA for puddles, adds DNA to vomit

* DNA now leaves traces in containers and those marked without don't show DNA on scan (except for puddles), gibbed parts have DNA

* Fix stupid metamorphic glass bug grrr

* Removed SpillableComponent since DnaSubstanceTraceComponent is used instead

* Removes data field from maps, adds DNA tracking for some missed items

* Give default value, fix missing values.

* Fixes recipe bug

* Review changes

* Make the Data list into a nullable type

* Revert map changes

* Move gibbed unknown DNA to forensicssystem
This commit is contained in:
SlamBamActionman
2024-08-09 01:27:27 +02:00
committed by GitHub
parent c43fcdfa06
commit 07174d0aaf
45 changed files with 307 additions and 66 deletions

View File

@@ -54,10 +54,16 @@ namespace Content.Client.Forensics
} }
text.AppendLine(); text.AppendLine();
text.AppendLine(Loc.GetString("forensic-scanner-interface-dnas")); text.AppendLine(Loc.GetString("forensic-scanner-interface-dnas"));
foreach (var dna in msg.DNAs) foreach (var dna in msg.TouchDNAs)
{ {
text.AppendLine(dna); text.AppendLine(dna);
} }
foreach (var dna in msg.SolutionDNAs)
{
if (msg.TouchDNAs.Contains(dna))
continue;
text.AppendLine(dna);
}
text.AppendLine(); text.AppendLine();
text.AppendLine(Loc.GetString("forensic-scanner-interface-residues")); text.AppendLine(Loc.GetString("forensic-scanner-interface-residues"));
foreach (var residue in msg.Residues) foreach (var residue in msg.Residues)

View File

@@ -8,10 +8,12 @@ using Content.Shared.Alert;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Prototypes;
using Content.Shared.Drunk; using Content.Shared.Drunk;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Forensics;
using Content.Shared.HealthExaminable; using Content.Shared.HealthExaminable;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Content.Shared.Popups; using Content.Shared.Popups;
@@ -54,6 +56,7 @@ public sealed class BloodstreamSystem : EntitySystem
SubscribeLocalEvent<BloodstreamComponent, ReactionAttemptEvent>(OnReactionAttempt); SubscribeLocalEvent<BloodstreamComponent, ReactionAttemptEvent>(OnReactionAttempt);
SubscribeLocalEvent<BloodstreamComponent, SolutionRelayEvent<ReactionAttemptEvent>>(OnReactionAttempt); SubscribeLocalEvent<BloodstreamComponent, SolutionRelayEvent<ReactionAttemptEvent>>(OnReactionAttempt);
SubscribeLocalEvent<BloodstreamComponent, RejuvenateEvent>(OnRejuvenate); SubscribeLocalEvent<BloodstreamComponent, RejuvenateEvent>(OnRejuvenate);
SubscribeLocalEvent<BloodstreamComponent, GenerateDnaEvent>(OnDnaGenerated);
} }
private void OnMapInit(Entity<BloodstreamComponent> ent, ref MapInitEvent args) private void OnMapInit(Entity<BloodstreamComponent> ent, ref MapInitEvent args)
@@ -183,8 +186,18 @@ public sealed class BloodstreamSystem : EntitySystem
bloodSolution.MaxVolume = entity.Comp.BloodMaxVolume; bloodSolution.MaxVolume = entity.Comp.BloodMaxVolume;
tempSolution.MaxVolume = entity.Comp.BleedPuddleThreshold * 4; // give some leeway, for chemstream as well tempSolution.MaxVolume = entity.Comp.BleedPuddleThreshold * 4; // give some leeway, for chemstream as well
// Ensure blood that should have DNA has it; must be run here, in case DnaComponent has not yet been initialized
if (TryComp<DnaComponent>(entity.Owner, out var donorComp) && donorComp.DNA == String.Empty)
{
donorComp.DNA = _forensicsSystem.GenerateDNA();
var ev = new GenerateDnaEvent { Owner = entity.Owner, DNA = donorComp.DNA };
RaiseLocalEvent(entity.Owner, ref ev);
}
// Fill blood solution with BLOOD // Fill blood solution with BLOOD
bloodSolution.AddReagent(entity.Comp.BloodReagent, entity.Comp.BloodMaxVolume - bloodSolution.Volume); bloodSolution.AddReagent(new ReagentId(entity.Comp.BloodReagent, GetEntityBloodData(entity.Owner)), entity.Comp.BloodMaxVolume - bloodSolution.Volume);
} }
private void OnDamageChanged(Entity<BloodstreamComponent> ent, ref DamageChangedEvent args) private void OnDamageChanged(Entity<BloodstreamComponent> ent, ref DamageChangedEvent args)
@@ -349,7 +362,7 @@ public sealed class BloodstreamSystem : EntitySystem
} }
if (amount >= 0) if (amount >= 0)
return _solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, amount, out _); return _solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, amount, null, GetEntityBloodData(uid));
// Removal is more involved, // Removal is more involved,
// since we also wanna handle moving it to the temporary solution // since we also wanna handle moving it to the temporary solution
@@ -370,10 +383,7 @@ public sealed class BloodstreamSystem : EntitySystem
tempSolution.AddSolution(temp, _prototypeManager); tempSolution.AddSolution(temp, _prototypeManager);
} }
if (_puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, sound: false)) _puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, sound: false);
{
_forensicsSystem.TransferDna(puddleUid, uid, canDnaBeCleaned: false);
}
tempSolution.RemoveAllSolution(); tempSolution.RemoveAllSolution();
} }
@@ -436,10 +446,7 @@ public sealed class BloodstreamSystem : EntitySystem
_solutionContainerSystem.RemoveAllSolution(component.TemporarySolution.Value); _solutionContainerSystem.RemoveAllSolution(component.TemporarySolution.Value);
} }
if (_puddleSystem.TrySpillAt(uid, tempSol, out var puddleUid)) _puddleSystem.TrySpillAt(uid, tempSol, out var puddleUid);
{
_forensicsSystem.TransferDna(puddleUid, uid, canDnaBeCleaned: false);
}
} }
/// <summary> /// <summary>
@@ -464,6 +471,40 @@ public sealed class BloodstreamSystem : EntitySystem
component.BloodReagent = reagent; component.BloodReagent = reagent;
if (currentVolume > 0) if (currentVolume > 0)
_solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, currentVolume, out _); _solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, currentVolume, null, GetEntityBloodData(uid));
}
private void OnDnaGenerated(Entity<BloodstreamComponent> entity, ref GenerateDnaEvent args)
{
if (_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.BloodSolutionName, ref entity.Comp.BloodSolution, out var bloodSolution))
{
foreach (var reagent in bloodSolution.Contents)
{
List<ReagentData> reagentData = reagent.Reagent.EnsureReagentData();
reagentData.RemoveAll(x => x is DnaData);
reagentData.AddRange(GetEntityBloodData(entity.Owner));
}
}
}
/// <summary>
/// Get the reagent data for blood that a specific entity should have.
/// </summary>
public List<ReagentData> GetEntityBloodData(EntityUid uid)
{
var bloodData = new List<ReagentData>();
var dnaData = new DnaData();
if (TryComp<DnaComponent>(uid, out var donorComp))
{
dnaData.DNA = donorComp.DNA;
} else
{
dnaData.DNA = Loc.GetString("forensics-dna-unknown");
}
bloodData.Add(dnaData);
return bloodData;
} }
} }

View File

@@ -123,7 +123,7 @@ namespace Content.Server.Chemistry.EntitySystems
var reagent = _protoManager.Index<ReagentPrototype>(reagentQuantity.Reagent.Prototype); var reagent = _protoManager.Index<ReagentPrototype>(reagentQuantity.Reagent.Prototype);
var reaction = var reaction =
reagent.ReactionTile(tile, (reagentQuantity.Quantity / vapor.TransferAmount) * 0.25f, EntityManager); reagent.ReactionTile(tile, (reagentQuantity.Quantity / vapor.TransferAmount) * 0.25f, EntityManager, reagentQuantity.Reagent.Data);
if (reaction > reagentQuantity.Quantity) if (reaction > reagentQuantity.Quantity)
{ {

View File

@@ -21,10 +21,12 @@ public sealed partial class CleanDecalsReaction : ITileReaction
[DataField] [DataField]
public FixedPoint2 CleanCost { get; private set; } = FixedPoint2.New(0.25f); public FixedPoint2 CleanCost { get; private set; } = FixedPoint2.New(0.25f);
public FixedPoint2 TileReact(TileRef tile, public FixedPoint2 TileReact(TileRef tile,
ReagentPrototype reagent, ReagentPrototype reagent,
FixedPoint2 reactVolume, FixedPoint2 reactVolume,
IEntityManager entityManager) IEntityManager entityManager,
List<ReagentData>? data)
{ {
if (reactVolume <= CleanCost || if (reactVolume <= CleanCost ||
!entityManager.TryGetComponent<MapGridComponent>(tile.GridUid, out var grid) || !entityManager.TryGetComponent<MapGridComponent>(tile.GridUid, out var grid) ||

View File

@@ -34,7 +34,8 @@ public sealed partial class CleanTileReaction : ITileReaction
FixedPoint2 ITileReaction.TileReact(TileRef tile, FixedPoint2 ITileReaction.TileReact(TileRef tile,
ReagentPrototype reagent, ReagentPrototype reagent,
FixedPoint2 reactVolume, FixedPoint2 reactVolume,
IEntityManager entityManager) IEntityManager entityManager
, List<ReagentData>? data)
{ {
var entities = entityManager.System<EntityLookupSystem>().GetLocalEntitiesIntersecting(tile, 0f).ToArray(); var entities = entityManager.System<EntityLookupSystem>().GetLocalEntitiesIntersecting(tile, 0f).ToArray();
var puddleQuery = entityManager.GetEntityQuery<PuddleComponent>(); var puddleQuery = entityManager.GetEntityQuery<PuddleComponent>();

View File

@@ -38,7 +38,8 @@ public sealed partial class CreateEntityTileReaction : ITileReaction
public FixedPoint2 TileReact(TileRef tile, public FixedPoint2 TileReact(TileRef tile,
ReagentPrototype reagent, ReagentPrototype reagent,
FixedPoint2 reactVolume, FixedPoint2 reactVolume,
IEntityManager entityManager) IEntityManager entityManager,
List<ReagentData>? data)
{ {
if (reactVolume >= Usage) if (reactVolume >= Usage)
{ {

View File

@@ -17,7 +17,8 @@ namespace Content.Server.Chemistry.TileReactions
public FixedPoint2 TileReact(TileRef tile, public FixedPoint2 TileReact(TileRef tile,
ReagentPrototype reagent, ReagentPrototype reagent,
FixedPoint2 reactVolume, FixedPoint2 reactVolume,
IEntityManager entityManager) IEntityManager entityManager,
List<ReagentData>? data)
{ {
if (reactVolume <= FixedPoint2.Zero || tile.Tile.IsEmpty) if (reactVolume <= FixedPoint2.Zero || tile.Tile.IsEmpty)
return FixedPoint2.Zero; return FixedPoint2.Zero;

View File

@@ -16,7 +16,8 @@ namespace Content.Server.Chemistry.TileReactions
public FixedPoint2 TileReact(TileRef tile, public FixedPoint2 TileReact(TileRef tile,
ReagentPrototype reagent, ReagentPrototype reagent,
FixedPoint2 reactVolume, FixedPoint2 reactVolume,
IEntityManager entityManager) IEntityManager entityManager,
List<ReagentData>? data)
{ {
if (reactVolume <= FixedPoint2.Zero || tile.Tile.IsEmpty) if (reactVolume <= FixedPoint2.Zero || tile.Tile.IsEmpty)
return FixedPoint2.Zero; return FixedPoint2.Zero;

View File

@@ -1,4 +1,4 @@
using Content.Server.Maps; using Content.Server.Maps;
using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
@@ -15,7 +15,8 @@ public sealed partial class PryTileReaction : ITileReaction
public FixedPoint2 TileReact(TileRef tile, public FixedPoint2 TileReact(TileRef tile,
ReagentPrototype reagent, ReagentPrototype reagent,
FixedPoint2 reactVolume, FixedPoint2 reactVolume,
IEntityManager entityManager) IEntityManager entityManager,
List<ReagentData>? data)
{ {
var sys = entityManager.System<TileSystem>(); var sys = entityManager.System<TileSystem>();
sys.PryTile(tile); sys.PryTile(tile);

View File

@@ -15,13 +15,14 @@ namespace Content.Server.Chemistry.TileReactions
public FixedPoint2 TileReact(TileRef tile, public FixedPoint2 TileReact(TileRef tile,
ReagentPrototype reagent, ReagentPrototype reagent,
FixedPoint2 reactVolume, FixedPoint2 reactVolume,
IEntityManager entityManager) IEntityManager entityManager,
List<ReagentData>? data)
{ {
var spillSystem = entityManager.System<PuddleSystem>(); var spillSystem = entityManager.System<PuddleSystem>();
if (reactVolume < 5 || !spillSystem.TryGetPuddle(tile, out _)) if (reactVolume < 5 || !spillSystem.TryGetPuddle(tile, out _))
return FixedPoint2.Zero; return FixedPoint2.Zero;
return spillSystem.TrySpillAt(tile, new Solution(reagent.ID, reactVolume), out _, sound: false, tileReact: false) return spillSystem.TrySpillAt(tile, new Solution(reagent.ID, reactVolume, data), out _, sound: false, tileReact: false)
? reactVolume ? reactVolume
: FixedPoint2.Zero; : FixedPoint2.Zero;
} }

View File

@@ -29,13 +29,14 @@ namespace Content.Server.Chemistry.TileReactions
public FixedPoint2 TileReact(TileRef tile, public FixedPoint2 TileReact(TileRef tile,
ReagentPrototype reagent, ReagentPrototype reagent,
FixedPoint2 reactVolume, FixedPoint2 reactVolume,
IEntityManager entityManager) IEntityManager entityManager,
List<ReagentData>? data)
{ {
if (reactVolume < 5) if (reactVolume < 5)
return FixedPoint2.Zero; return FixedPoint2.Zero;
if (entityManager.EntitySysManager.GetEntitySystem<PuddleSystem>() if (entityManager.EntitySysManager.GetEntitySystem<PuddleSystem>()
.TrySpillAt(tile, new Solution(reagent.ID, reactVolume), out var puddleUid, false, false)) .TrySpillAt(tile, new Solution(reagent.ID, reactVolume, data), out var puddleUid, false, false))
{ {
var slippery = entityManager.EnsureComponent<SlipperyComponent>(puddleUid); var slippery = entityManager.EnsureComponent<SlipperyComponent>(puddleUid);
slippery.LaunchForwardsMultiplier = _launchForwardsMultiplier; slippery.LaunchForwardsMultiplier = _launchForwardsMultiplier;

View File

@@ -712,7 +712,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
{ {
var (reagent, quantity) = solution.Contents[i]; var (reagent, quantity) = solution.Contents[i];
var proto = _prototypeManager.Index<ReagentPrototype>(reagent.Prototype); var proto = _prototypeManager.Index<ReagentPrototype>(reagent.Prototype);
var removed = proto.ReactionTile(tileRef, quantity, EntityManager); var removed = proto.ReactionTile(tileRef, quantity, EntityManager, reagent.Data);
if (removed <= FixedPoint2.Zero) if (removed <= FixedPoint2.Zero)
continue; continue;

View File

@@ -315,7 +315,7 @@ public sealed class SmokeSystem : EntitySystem
continue; continue;
var reagent = _prototype.Index<ReagentPrototype>(reagentQuantity.Reagent.Prototype); var reagent = _prototype.Index<ReagentPrototype>(reagentQuantity.Reagent.Prototype);
reagent.ReactionTile(tile, reagentQuantity.Quantity, EntityManager); reagent.ReactionTile(tile, reagentQuantity.Quantity, EntityManager, reagentQuantity.Reagent.Data);
} }
} }

View File

@@ -0,0 +1,9 @@
namespace Content.Server.Forensics;
/// <summary>
/// This component stops the entity from leaving finger prints,
/// usually so fibres can be left instead.
/// </summary>
[RegisterComponent]
public sealed partial class DnaSubstanceTraceComponent : Component
{ }

View File

@@ -26,7 +26,13 @@ namespace Content.Server.Forensics
/// DNA that the forensic scanner found from the <see cref="DNAComponent"/> on an entity. /// DNA that the forensic scanner found from the <see cref="DNAComponent"/> on an entity.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadOnly), DataField("dnas")] [ViewVariables(VVAccess.ReadOnly), DataField("dnas")]
public List<string> DNAs = new(); public List<string> TouchDNAs = new();
/// <summary>
/// DNA that the forensic scanner found from the solution containers in an entity.
/// </summary>
[ViewVariables(VVAccess.ReadOnly), DataField]
public List<string> SolutionDNAs = new();
/// <summary> /// <summary>
/// Residue that the forensic scanner found from the <see cref="ForensicsComponent"/> on an entity. /// Residue that the forensic scanner found from the <see cref="ForensicsComponent"/> on an entity.

View File

@@ -3,16 +3,19 @@ using System.Text;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.UserInterface; using Content.Shared.UserInterface;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Fluids.Components;
using Content.Shared.Forensics; using Content.Shared.Forensics;
using Content.Shared.Hands.EntitySystems; using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Paper; using Content.Shared.Paper;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Content.Shared.Tag;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Content.Server.Chemistry.Containers.EntitySystems;
// todo: remove this stinky LINQy // todo: remove this stinky LINQy
namespace Content.Server.Forensics namespace Content.Server.Forensics
@@ -27,6 +30,9 @@ namespace Content.Server.Forensics
[Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly ForensicsSystem _forensicsSystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly TagSystem _tag = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -46,7 +52,8 @@ namespace Content.Server.Forensics
var state = new ForensicScannerBoundUserInterfaceState( var state = new ForensicScannerBoundUserInterfaceState(
component.Fingerprints, component.Fingerprints,
component.Fibers, component.Fibers,
component.DNAs, component.TouchDNAs,
component.SolutionDNAs,
component.Residues, component.Residues,
component.LastScannedName, component.LastScannedName,
component.PrintCooldown, component.PrintCooldown,
@@ -69,18 +76,25 @@ namespace Content.Server.Forensics
{ {
scanner.Fingerprints = new(); scanner.Fingerprints = new();
scanner.Fibers = new(); scanner.Fibers = new();
scanner.DNAs = new(); scanner.TouchDNAs = new();
scanner.Residues = new(); scanner.Residues = new();
} }
else else
{ {
scanner.Fingerprints = forensics.Fingerprints.ToList(); scanner.Fingerprints = forensics.Fingerprints.ToList();
scanner.Fibers = forensics.Fibers.ToList(); scanner.Fibers = forensics.Fibers.ToList();
scanner.DNAs = forensics.DNAs.ToList(); scanner.TouchDNAs = forensics.DNAs.ToList();
scanner.Residues = forensics.Residues.ToList(); scanner.Residues = forensics.Residues.ToList();
} }
if (_tag.HasTag(args.Args.Target.Value, "DNASolutionScannable"))
{
scanner.SolutionDNAs = _forensicsSystem.GetSolutionsDNA(args.Args.Target.Value);
} else
{
scanner.SolutionDNAs = new();
}
scanner.LastScannedName = MetaData(args.Args.Target.Value).EntityName; scanner.LastScannedName = MetaData(args.Args.Target.Value).EntityName;
} }
@@ -206,10 +220,17 @@ namespace Content.Server.Forensics
} }
text.AppendLine(); text.AppendLine();
text.AppendLine(Loc.GetString("forensic-scanner-interface-dnas")); text.AppendLine(Loc.GetString("forensic-scanner-interface-dnas"));
foreach (var dna in component.DNAs) foreach (var dna in component.TouchDNAs)
{ {
text.AppendLine(dna); text.AppendLine(dna);
} }
foreach (var dna in component.SolutionDNAs)
{
Log.Debug(dna);
if (component.TouchDNAs.Contains(dna))
continue;
text.AppendLine(dna);
}
text.AppendLine(); text.AppendLine();
text.AppendLine(Loc.GetString("forensic-scanner-interface-residues")); text.AppendLine(Loc.GetString("forensic-scanner-interface-residues"));
foreach (var residue in component.Residues) foreach (var residue in component.Residues)
@@ -232,7 +253,8 @@ namespace Content.Server.Forensics
{ {
component.Fingerprints = new(); component.Fingerprints = new();
component.Fibers = new(); component.Fibers = new();
component.DNAs = new(); component.TouchDNAs = new();
component.SolutionDNAs = new();
component.LastScannedName = string.Empty; component.LastScannedName = string.Empty;
UpdateUserInterface(uid, component); UpdateUserInterface(uid, component);

View File

@@ -1,11 +1,16 @@
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.DoAfter; using Content.Server.DoAfter;
using Content.Server.Fluids.EntitySystems; using Content.Server.Fluids.EntitySystems;
using Content.Server.Forensics.Components; using Content.Server.Forensics.Components;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Fluids.Components;
using Content.Shared.Forensics; using Content.Shared.Forensics;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Events;
@@ -23,20 +28,35 @@ namespace Content.Server.Forensics
[Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
public override void Initialize() public override void Initialize()
{ {
SubscribeLocalEvent<FingerprintComponent, ContactInteractionEvent>(OnInteract); SubscribeLocalEvent<FingerprintComponent, ContactInteractionEvent>(OnInteract);
SubscribeLocalEvent<FingerprintComponent, MapInitEvent>(OnFingerprintInit); SubscribeLocalEvent<FingerprintComponent, MapInitEvent>(OnFingerprintInit);
SubscribeLocalEvent<DnaComponent, MapInitEvent>(OnDNAInit); SubscribeLocalEvent<DnaComponent, MapInitEvent>(OnDNAInit);
SubscribeLocalEvent<DnaComponent, BeingGibbedEvent>(OnBeingGibbed); SubscribeLocalEvent<ForensicsComponent, BeingGibbedEvent>(OnBeingGibbed);
SubscribeLocalEvent<ForensicsComponent, MeleeHitEvent>(OnMeleeHit); SubscribeLocalEvent<ForensicsComponent, MeleeHitEvent>(OnMeleeHit);
SubscribeLocalEvent<ForensicsComponent, GotRehydratedEvent>(OnRehydrated); SubscribeLocalEvent<ForensicsComponent, GotRehydratedEvent>(OnRehydrated);
SubscribeLocalEvent<CleansForensicsComponent, AfterInteractEvent>(OnAfterInteract, after: new[] { typeof(AbsorbentSystem) }); SubscribeLocalEvent<CleansForensicsComponent, AfterInteractEvent>(OnAfterInteract, after: new[] { typeof(AbsorbentSystem) });
SubscribeLocalEvent<ForensicsComponent, CleanForensicsDoAfterEvent>(OnCleanForensicsDoAfter); SubscribeLocalEvent<ForensicsComponent, CleanForensicsDoAfterEvent>(OnCleanForensicsDoAfter);
SubscribeLocalEvent<DnaComponent, TransferDnaEvent>(OnTransferDnaEvent); SubscribeLocalEvent<DnaComponent, TransferDnaEvent>(OnTransferDnaEvent);
SubscribeLocalEvent<DnaSubstanceTraceComponent, SolutionContainerChangedEvent>(OnSolutionChanged);
SubscribeLocalEvent<CleansForensicsComponent, GetVerbsEvent<UtilityVerb>>(OnUtilityVerb); SubscribeLocalEvent<CleansForensicsComponent, GetVerbsEvent<UtilityVerb>>(OnUtilityVerb);
}
private void OnSolutionChanged(Entity<DnaSubstanceTraceComponent> ent, ref SolutionContainerChangedEvent ev)
{
var soln = GetSolutionsDNA(ev.Solution);
if (soln.Count > 0)
{
var comp = EnsureComp<ForensicsComponent>(ent.Owner);
foreach (string dna in soln)
{
comp.DNAs.Add(dna);
}
}
} }
private void OnInteract(EntityUid uid, FingerprintComponent component, ContactInteractionEvent args) private void OnInteract(EntityUid uid, FingerprintComponent component, ContactInteractionEvent args)
@@ -51,15 +71,26 @@ namespace Content.Server.Forensics
private void OnDNAInit(EntityUid uid, DnaComponent component, MapInitEvent args) private void OnDNAInit(EntityUid uid, DnaComponent component, MapInitEvent args)
{ {
component.DNA = GenerateDNA(); if (component.DNA == String.Empty)
{
component.DNA = GenerateDNA();
var ev = new GenerateDnaEvent { Owner = uid, DNA = component.DNA };
RaiseLocalEvent(uid, ref ev);
}
} }
private void OnBeingGibbed(EntityUid uid, DnaComponent component, BeingGibbedEvent args) private void OnBeingGibbed(EntityUid uid, ForensicsComponent component, BeingGibbedEvent args)
{ {
string dna = Loc.GetString("forensics-dna-unknown");
if (TryComp(uid, out DnaComponent? dnaComp))
dna = dnaComp.DNA;
foreach (EntityUid part in args.GibbedParts) foreach (EntityUid part in args.GibbedParts)
{ {
var partComp = EnsureComp<ForensicsComponent>(part); var partComp = EnsureComp<ForensicsComponent>(part);
partComp.DNAs.Add(component.DNA); partComp.DNAs.Add(dna);
partComp.CanDnaBeCleaned = false; partComp.CanDnaBeCleaned = false;
} }
} }
@@ -106,6 +137,34 @@ namespace Content.Server.Forensics
} }
} }
public List<string> GetSolutionsDNA(EntityUid uid)
{
List<string> list = new();
if (TryComp<SolutionContainerManagerComponent>(uid, out var comp))
{
foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((uid, comp)))
{
list.AddRange(GetSolutionsDNA(soln.Comp.Solution));
}
}
return list;
}
public List<string> GetSolutionsDNA(Solution soln)
{
List<string> list = new();
foreach (var reagent in soln.Contents)
{
foreach (var data in reagent.Reagent.EnsureReagentData())
{
if (data is DnaData)
{
list.Add(((DnaData) data).DNA);
}
}
}
return list;
}
private void OnAfterInteract(Entity<CleansForensicsComponent> cleanForensicsEntity, ref AfterInteractEvent args) private void OnAfterInteract(Entity<CleansForensicsComponent> cleanForensicsEntity, ref AfterInteractEvent args)
{ {
if (args.Handled || !args.CanReach || args.Target == null) if (args.Handled || !args.CanReach || args.Target == null)

View File

@@ -1,10 +1,11 @@
using Content.Server.Cuffs; using Content.Server.Cuffs;
using Content.Server.Forensics; using Content.Server.Forensics;
using Content.Server.Humanoid; using Content.Server.Humanoid;
using Content.Server.Implants.Components; using Content.Server.Implants.Components;
using Content.Server.Store.Components; using Content.Server.Store.Components;
using Content.Server.Store.Systems; using Content.Server.Store.Systems;
using Content.Shared.Cuffs.Components; using Content.Shared.Cuffs.Components;
using Content.Shared.Forensics;
using Content.Shared.Humanoid; using Content.Shared.Humanoid;
using Content.Shared.Implants; using Content.Shared.Implants;
using Content.Shared.Implants.Components; using Content.Shared.Implants.Components;
@@ -212,6 +213,9 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem
if (TryComp<DnaComponent>(ent, out var dna)) if (TryComp<DnaComponent>(ent, out var dna))
{ {
dna.DNA = _forensicsSystem.GenerateDNA(); dna.DNA = _forensicsSystem.GenerateDNA();
var ev = new GenerateDnaEvent { Owner = ent, DNA = dna.DNA };
RaiseLocalEvent(ent, ref ev);
} }
if (TryComp<FingerprintComponent>(ent, out var fingerprint)) if (TryComp<FingerprintComponent>(ent, out var fingerprint))
{ {

View File

@@ -6,6 +6,7 @@ using Content.Server.Forensics;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Stunnable; using Content.Server.Stunnable;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Nutrition.EntitySystems;
@@ -28,6 +29,7 @@ namespace Content.Server.Medical
[Dependency] private readonly StunSystem _stun = default!; [Dependency] private readonly StunSystem _stun = default!;
[Dependency] private readonly ThirstSystem _thirst = default!; [Dependency] private readonly ThirstSystem _thirst = default!;
[Dependency] private readonly ForensicsSystem _forensics = default!; [Dependency] private readonly ForensicsSystem _forensics = default!;
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
/// <summary> /// <summary>
/// Make an entity vomit, if they have a stomach. /// Make an entity vomit, if they have a stomach.
@@ -83,7 +85,7 @@ namespace Content.Server.Medical
} }
// Makes a vomit solution the size of 90% of the chemicals removed from the chemstream // Makes a vomit solution the size of 90% of the chemicals removed from the chemstream
solution.AddReagent("Vomit", vomitAmount); // TODO: Dehardcode vomit prototype solution.AddReagent(new ReagentId("Vomit", _bloodstream.GetEntityBloodData(uid)), vomitAmount); // TODO: Dehardcode vomit prototype
} }
if (_puddle.TrySpillAt(uid, solution, out var puddle, false)) if (_puddle.TrySpillAt(uid, solution, out var puddle, false))

View File

@@ -147,7 +147,7 @@ namespace Content.Shared.Chemistry.Components
/// </summary> /// </summary>
/// <param name="prototype">The prototype ID of the reagent to add.</param> /// <param name="prototype">The prototype ID of the reagent to add.</param>
/// <param name="quantity">The quantity in milli-units.</param> /// <param name="quantity">The quantity in milli-units.</param>
public Solution(string prototype, FixedPoint2 quantity, ReagentData? data = null) : this() public Solution(string prototype, FixedPoint2 quantity, List<ReagentData>? data = null) : this()
{ {
AddReagent(new ReagentId(prototype, data), quantity); AddReagent(new ReagentId(prototype, data), quantity);
} }
@@ -243,7 +243,7 @@ namespace Content.Shared.Chemistry.Components
return false; return false;
} }
public bool ContainsReagent(string reagentId, ReagentData? data) public bool ContainsReagent(string reagentId, List<ReagentData>? data)
=> ContainsReagent(new(reagentId, data)); => ContainsReagent(new(reagentId, data));
public bool TryGetReagent(ReagentId id, out ReagentQuantity quantity) public bool TryGetReagent(ReagentId id, out ReagentQuantity quantity)
@@ -404,7 +404,7 @@ namespace Content.Shared.Chemistry.Components
/// </summary> /// </summary>
/// <param name="proto">The prototype of the reagent to add.</param> /// <param name="proto">The prototype of the reagent to add.</param>
/// <param name="quantity">The quantity in milli-units.</param> /// <param name="quantity">The quantity in milli-units.</param>
public void AddReagent(ReagentPrototype proto, FixedPoint2 quantity, float temperature, IPrototypeManager? protoMan, ReagentData? data = null) public void AddReagent(ReagentPrototype proto, FixedPoint2 quantity, float temperature, IPrototypeManager? protoMan, List<ReagentData>? data = null)
{ {
if (_heatCapacityDirty) if (_heatCapacityDirty)
UpdateHeatCapacity(protoMan); UpdateHeatCapacity(protoMan);
@@ -489,7 +489,7 @@ namespace Content.Shared.Chemistry.Components
{ {
var (reagent, curQuantity) = Contents[i]; var (reagent, curQuantity) = Contents[i];
if(reagent != toRemove.Reagent) if(reagent.Prototype != toRemove.Reagent.Prototype)
continue; continue;
var newQuantity = curQuantity - toRemove.Quantity; var newQuantity = curQuantity - toRemove.Quantity;
@@ -523,7 +523,7 @@ namespace Content.Shared.Chemistry.Components
/// <param name="prototype">The prototype of the reagent to be removed.</param> /// <param name="prototype">The prototype of the reagent to be removed.</param>
/// <param name="quantity">The amount of reagent to remove.</param> /// <param name="quantity">The amount of reagent to remove.</param>
/// <returns>How much reagent was actually removed. Zero if the reagent is not present on the solution.</returns> /// <returns>How much reagent was actually removed. Zero if the reagent is not present on the solution.</returns>
public FixedPoint2 RemoveReagent(string prototype, FixedPoint2 quantity, ReagentData? data = null) public FixedPoint2 RemoveReagent(string prototype, FixedPoint2 quantity, List<ReagentData>? data = null)
{ {
return RemoveReagent(new ReagentQuantity(prototype, quantity, data)); return RemoveReagent(new ReagentQuantity(prototype, quantity, data));
} }

View File

@@ -452,7 +452,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
/// <param name="quantity">The amount of reagent to add.</param> /// <param name="quantity">The amount of reagent to add.</param>
/// <returns>If all the reagent could be added.</returns> /// <returns>If all the reagent could be added.</returns>
[PublicAPI] [PublicAPI]
public bool TryAddReagent(Entity<SolutionComponent> soln, string prototype, FixedPoint2 quantity, float? temperature = null, ReagentData? data = null) public bool TryAddReagent(Entity<SolutionComponent> soln, string prototype, FixedPoint2 quantity, float? temperature = null, List<ReagentData>? data = null)
=> TryAddReagent(soln, new ReagentQuantity(prototype, quantity, data), out _, temperature); => TryAddReagent(soln, new ReagentQuantity(prototype, quantity, data), out _, temperature);
/// <summary> /// <summary>
@@ -464,7 +464,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
/// <param name="quantity">The amount of reagent to add.</param> /// <param name="quantity">The amount of reagent to add.</param>
/// <param name="acceptedQuantity">The amount of reagent successfully added.</param> /// <param name="acceptedQuantity">The amount of reagent successfully added.</param>
/// <returns>If all the reagent could be added.</returns> /// <returns>If all the reagent could be added.</returns>
public bool TryAddReagent(Entity<SolutionComponent> soln, string prototype, FixedPoint2 quantity, out FixedPoint2 acceptedQuantity, float? temperature = null, ReagentData? data = null) public bool TryAddReagent(Entity<SolutionComponent> soln, string prototype, FixedPoint2 quantity, out FixedPoint2 acceptedQuantity, float? temperature = null, List<ReagentData>? data = null)
{ {
var reagent = new ReagentQuantity(prototype, quantity, data); var reagent = new ReagentQuantity(prototype, quantity, data);
return TryAddReagent(soln, reagent, out acceptedQuantity, temperature); return TryAddReagent(soln, reagent, out acceptedQuantity, temperature);
@@ -513,7 +513,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
/// <param name="prototype">The Id of the reagent to remove.</param> /// <param name="prototype">The Id of the reagent to remove.</param>
/// <param name="quantity">The amount of reagent to remove.</param> /// <param name="quantity">The amount of reagent to remove.</param>
/// <returns>If the reagent to remove was found in the container.</returns> /// <returns>If the reagent to remove was found in the container.</returns>
public bool RemoveReagent(Entity<SolutionComponent> soln, string prototype, FixedPoint2 quantity, ReagentData? data = null) public bool RemoveReagent(Entity<SolutionComponent> soln, string prototype, FixedPoint2 quantity, List<ReagentData>? data = null)
{ {
return RemoveReagent(soln, new ReagentQuantity(prototype, quantity, data)); return RemoveReagent(soln, new ReagentQuantity(prototype, quantity, data));
} }

View File

@@ -1,4 +1,4 @@
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.Map; using Robust.Shared.Map;
@@ -9,6 +9,7 @@ namespace Content.Shared.Chemistry.Reaction
FixedPoint2 TileReact(TileRef tile, FixedPoint2 TileReact(TileRef tile,
ReagentPrototype reagent, ReagentPrototype reagent,
FixedPoint2 reactVolume, FixedPoint2 reactVolume,
IEntityManager entityManager); IEntityManager entityManager,
List<ReagentData>? data = null);
} }
} }

View File

@@ -0,0 +1,28 @@
using Content.Shared.FixedPoint;
using Robust.Shared.Serialization;
namespace Content.Shared.Chemistry.Reagent;
[ImplicitDataDefinitionForInheritors, Serializable, NetSerializable]
public sealed partial class DnaData : ReagentData
{
[DataField]
public string DNA = String.Empty;
public override ReagentData Clone() => this;
public override bool Equals(ReagentData? other)
{
if (other == null)
{
return false;
}
return ((DnaData) other).DNA == DNA;
}
public override int GetHashCode()
{
return DNA.GetHashCode();
}
}

View File

@@ -1,6 +1,7 @@
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using System.Linq;
namespace Content.Shared.Chemistry.Reagent; namespace Content.Shared.Chemistry.Reagent;
@@ -20,17 +21,23 @@ public partial struct ReagentId : IEquatable<ReagentId>
/// Any additional data that is unique to this reagent type. E.g., for blood this could be DNA data. /// Any additional data that is unique to this reagent type. E.g., for blood this could be DNA data.
/// </summary> /// </summary>
[DataField("data")] [DataField("data")]
public ReagentData? Data { get; private set; } public List<ReagentData>? Data { get; private set; } = new();
public ReagentId(string prototype, ReagentData? data) public ReagentId(string prototype, List<ReagentData>? data)
{ {
Prototype = prototype; Prototype = prototype;
Data = data; Data = data ?? new();
} }
public ReagentId() public ReagentId()
{ {
Prototype = default!; Prototype = default!;
Data = new();
}
public List<ReagentData> EnsureReagentData()
{
return (Data != null) ? Data : new List<ReagentData>();
} }
public bool Equals(ReagentId other) public bool Equals(ReagentId other)
@@ -44,13 +51,13 @@ public partial struct ReagentId : IEquatable<ReagentId>
if (other.Data == null) if (other.Data == null)
return false; return false;
if (Data.GetType() != other.Data.GetType()) if (Data.Except(other.Data).Any() || other.Data.Except(Data).Any() || Data.Count != other.Data.Count)
return false; return false;
return Data.Equals(other.Data); return true;
} }
public bool Equals(string prototype, ReagentData? otherData = null) public bool Equals(string prototype, List<ReagentData>? otherData = null)
{ {
if (Prototype != prototype) if (Prototype != prototype)
return false; return false;
@@ -73,12 +80,12 @@ public partial struct ReagentId : IEquatable<ReagentId>
public string ToString(FixedPoint2 quantity) public string ToString(FixedPoint2 quantity)
{ {
return Data?.ToString(Prototype, quantity) ?? $"{Prototype}:{quantity}"; return $"{Prototype}:{quantity}";
} }
public override string ToString() public override string ToString()
{ {
return Data?.ToString(Prototype) ?? Prototype; return $"{Prototype}";
} }
public static bool operator ==(ReagentId left, ReagentId right) public static bool operator ==(ReagentId left, ReagentId right)

View File

@@ -143,7 +143,7 @@ namespace Content.Shared.Chemistry.Reagent
[DataField] [DataField]
public SoundSpecifier FootstepSound = new SoundCollectionSpecifier("FootstepWater", AudioParams.Default.WithVolume(6)); public SoundSpecifier FootstepSound = new SoundCollectionSpecifier("FootstepWater", AudioParams.Default.WithVolume(6));
public FixedPoint2 ReactionTile(TileRef tile, FixedPoint2 reactVolume, IEntityManager entityManager) public FixedPoint2 ReactionTile(TileRef tile, FixedPoint2 reactVolume, IEntityManager entityManager, List<ReagentData>? data)
{ {
var removed = FixedPoint2.Zero; var removed = FixedPoint2.Zero;
@@ -152,7 +152,7 @@ namespace Content.Shared.Chemistry.Reagent
foreach (var reaction in TileReactions) foreach (var reaction in TileReactions)
{ {
removed += reaction.TileReact(tile, this, reactVolume - removed, entityManager); removed += reaction.TileReact(tile, this, reactVolume - removed, entityManager, data);
if (removed > reactVolume) if (removed > reactVolume)
throw new Exception("Removed more than we have!"); throw new Exception("Removed more than we have!");

View File

@@ -17,7 +17,7 @@ public partial struct ReagentQuantity : IEquatable<ReagentQuantity>
[ViewVariables] [ViewVariables]
public ReagentId Reagent { get; private set; } public ReagentId Reagent { get; private set; }
public ReagentQuantity(string reagentId, FixedPoint2 quantity, ReagentData? data) public ReagentQuantity(string reagentId, FixedPoint2 quantity, List<ReagentData>? data = null)
: this(new ReagentId(reagentId, data), quantity) : this(new ReagentId(reagentId, data), quantity)
{ {
} }
@@ -37,7 +37,7 @@ public partial struct ReagentQuantity : IEquatable<ReagentQuantity>
return Reagent.ToString(Quantity); return Reagent.ToString(Quantity);
} }
public void Deconstruct(out string prototype, out FixedPoint2 quantity, out ReagentData? data) public void Deconstruct(out string prototype, out FixedPoint2 quantity, out List<ReagentData>? data)
{ {
prototype = Reagent.Prototype; prototype = Reagent.Prototype;
quantity = Quantity; quantity = Quantity;

View File

@@ -51,3 +51,20 @@ public record struct TransferDnaEvent()
/// </summary> /// </summary>
public bool CanDnaBeCleaned = true; public bool CanDnaBeCleaned = true;
} }
/// <summary>
/// An event to generate and act upon new DNA for an entity.
/// </summary>
[ByRefEvent]
public record struct GenerateDnaEvent()
{
/// <summary>
/// The entity getting new DNA.
/// </summary>
public EntityUid Owner;
/// <summary>
/// The generated DNA.
/// </summary>
public string DNA;
}

View File

@@ -7,7 +7,8 @@ namespace Content.Shared.Forensics
{ {
public readonly List<string> Fingerprints = new(); public readonly List<string> Fingerprints = new();
public readonly List<string> Fibers = new(); public readonly List<string> Fibers = new();
public readonly List<string> DNAs = new(); public readonly List<string> TouchDNAs = new();
public readonly List<string> SolutionDNAs = new();
public readonly List<string> Residues = new(); public readonly List<string> Residues = new();
public readonly string LastScannedName = string.Empty; public readonly string LastScannedName = string.Empty;
public readonly TimeSpan PrintCooldown = TimeSpan.Zero; public readonly TimeSpan PrintCooldown = TimeSpan.Zero;
@@ -16,7 +17,8 @@ namespace Content.Shared.Forensics
public ForensicScannerBoundUserInterfaceState( public ForensicScannerBoundUserInterfaceState(
List<string> fingerprints, List<string> fingerprints,
List<string> fibers, List<string> fibers,
List<string> dnas, List<string> touchDnas,
List<string> solutionDnas,
List<string> residues, List<string> residues,
string lastScannedName, string lastScannedName,
TimeSpan printCooldown, TimeSpan printCooldown,
@@ -24,7 +26,8 @@ namespace Content.Shared.Forensics
{ {
Fingerprints = fingerprints; Fingerprints = fingerprints;
Fibers = fibers; Fibers = fibers;
DNAs = dnas; TouchDNAs = touchDnas;
SolutionDNAs = solutionDnas;
Residues = residues; Residues = residues;
LastScannedName = lastScannedName; LastScannedName = lastScannedName;
PrintCooldown = printCooldown; PrintCooldown = printCooldown;

View File

@@ -23,6 +23,8 @@ forensic-scanner-verb-message = Perform a forensic scan
forensic-pad-fingerprint-name = {$entity}'s fingerprints forensic-pad-fingerprint-name = {$entity}'s fingerprints
forensic-pad-gloves-name = fibers from {$entity} forensic-pad-gloves-name = fibers from {$entity}
forensics-dna-unknown = unknown DNA
forensics-verb-text = Remove evidence forensics-verb-text = Remove evidence
forensics-verb-message = Remove fingerprints and DNA residues from the object! forensics-verb-message = Remove fingerprints and DNA residues from the object!
forensics-cleaning = You begin cleaning the evidence off of {THE($target)}... forensics-cleaning = You begin cleaning the evidence off of {THE($target)}...

View File

@@ -162,3 +162,6 @@
solution: puddle solution: puddle
- type: BadDrink - type: BadDrink
- type: IgnoresFingerprints - type: IgnoresFingerprints
- type: Tag
tags:
- DNASolutionScannable

View File

@@ -42,6 +42,7 @@
#In future maybe add generic plastic scrap trash/debris #In future maybe add generic plastic scrap trash/debris
- type: TrashOnSolutionEmpty - type: TrashOnSolutionEmpty
solution: drink solution: drink
- type: DnaSubstanceTrace
- type: entity - type: entity
parent: DrinkCartonBaseFull parent: DrinkCartonBaseFull

View File

@@ -35,6 +35,7 @@
interfaces: interfaces:
enum.TransferAmountUiKey.Key: enum.TransferAmountUiKey.Key:
type: TransferAmountBoundUserInterface type: TransferAmountBoundUserInterface
- type: DnaSubstanceTrace
- type: entity - type: entity
parent: DrinkBase parent: DrinkBase

View File

@@ -39,6 +39,7 @@
- type: PhysicalComposition - type: PhysicalComposition
materialComposition: materialComposition:
Plastic: 100 Plastic: 100
- type: DnaSubstanceTrace
- type: PressurizedSolution - type: PressurizedSolution
solution: drink solution: drink
- type: Shakeable - type: Shakeable

View File

@@ -66,6 +66,7 @@
- type: Tag - type: Tag
tags: tags:
- DrinkCan - DrinkCan
- type: DnaSubstanceTrace
- type: entity - type: entity
parent: DrinkCanBaseFull parent: DrinkCanBaseFull

View File

@@ -38,6 +38,7 @@
damage: damage:
types: types:
Blunt: 0 Blunt: 0
- type: DnaSubstanceTrace
- type: entity - type: entity
parent: DrinkBaseCup parent: DrinkBaseCup

View File

@@ -34,6 +34,7 @@
- type: PhysicalComposition - type: PhysicalComposition
materialComposition: materialComposition:
Steel: 50 Steel: 50
- type: DnaSubstanceTrace
- type: ReactionMixer - type: ReactionMixer
mixOnInteract: false mixOnInteract: false
reactionTypes: reactionTypes:

View File

@@ -76,6 +76,7 @@
materialComposition: materialComposition:
Glass: 100 Glass: 100
- type: SpaceGarbage - type: SpaceGarbage
- type: DnaSubstanceTrace
- type: entity - type: entity
name: base empty bottle name: base empty bottle

View File

@@ -50,6 +50,7 @@
- type: GuideHelp - type: GuideHelp
guides: guides:
- Janitorial - Janitorial
- type: DnaSubstanceTrace
- type: entity - type: entity
parent: BaseItem parent: BaseItem
@@ -109,6 +110,7 @@
tags: tags:
- Mop - Mop
- MopAdv - MopAdv
- type: DnaSubstanceTrace
- type: entity - type: entity
name: wet floor sign name: wet floor sign
@@ -332,3 +334,4 @@
- type: CleansForensics - type: CleansForensics
- type: Fiber - type: Fiber
fiberColor: fibers-white fiberColor: fibers-white
- type: DnaSubstanceTrace

View File

@@ -82,6 +82,7 @@
- type: Tag - type: Tag
tags: tags:
- ChemDispensable - ChemDispensable
- type: DnaSubstanceTrace
- type: entity - type: entity
parent: Jug parent: Jug

View File

@@ -93,6 +93,7 @@
transferForensics: true transferForensics: true
- !type:DoActsBehavior - !type:DoActsBehavior
acts: [ "Destruction" ] acts: [ "Destruction" ]
- type: DnaSubstanceTrace
- type: entity - type: entity
parent: BaseChemistryEmptyBottle parent: BaseChemistryEmptyBottle

View File

@@ -101,6 +101,7 @@
acts: [ "Destruction" ] acts: [ "Destruction" ]
- type: Spillable - type: Spillable
solution: beaker solution: beaker
- type: DnaSubstanceTrace
- type: entity - type: entity
id: VestineChemistryVial id: VestineChemistryVial

View File

@@ -92,6 +92,7 @@
Blunt: 5 Blunt: 5
- type: StaticPrice - type: StaticPrice
price: 30 price: 30
- type: DnaSubstanceTrace
- type: entity - type: entity
parent: BaseItem parent: BaseItem
@@ -153,6 +154,7 @@
damageModifierSet: Glass damageModifierSet: Glass
- type: StaticPrice - type: StaticPrice
price: 30 price: 30
- type: DnaSubstanceTrace
- type: entity - type: entity
name: beaker name: beaker
@@ -303,6 +305,7 @@
inHandsFillBaseName: -fill- inHandsFillBaseName: -fill-
- type: StaticPrice - type: StaticPrice
price: 40 price: 40
- type: DnaSubstanceTrace
- type: Tag - type: Tag
tags: tags:
- Dropper - Dropper

View File

@@ -67,3 +67,4 @@
- type: PhysicalComposition - type: PhysicalComposition
materialComposition: materialComposition:
Plastic: 50 Plastic: 50
- type: DnaSubstanceTrace

View File

@@ -84,6 +84,7 @@
behaviors: behaviors:
- !type:DoActsBehavior - !type:DoActsBehavior
acts: ["Destruction"] acts: ["Destruction"]
- type: DnaSubstanceTrace
- type: Fixtures - type: Fixtures
fixtures: fixtures:
fix1: fix1:
@@ -334,3 +335,4 @@
- type: GuideHelp - type: GuideHelp
guides: guides:
- Janitorial - Janitorial
- type: DnaSubstanceTrace

View File

@@ -509,6 +509,9 @@
- type: Tag - type: Tag
id: DiscreteHealthAnalyzer #So construction recipes don't eat medical PDAs id: DiscreteHealthAnalyzer #So construction recipes don't eat medical PDAs
- type: Tag
id: DNASolutionScannable
- type: Tag - type: Tag
id: DockArrivals id: DockArrivals