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:
@@ -54,10 +54,16 @@ namespace Content.Client.Forensics
|
||||
}
|
||||
text.AppendLine();
|
||||
text.AppendLine(Loc.GetString("forensic-scanner-interface-dnas"));
|
||||
foreach (var dna in msg.DNAs)
|
||||
foreach (var dna in msg.TouchDNAs)
|
||||
{
|
||||
text.AppendLine(dna);
|
||||
}
|
||||
foreach (var dna in msg.SolutionDNAs)
|
||||
{
|
||||
if (msg.TouchDNAs.Contains(dna))
|
||||
continue;
|
||||
text.AppendLine(dna);
|
||||
}
|
||||
text.AppendLine();
|
||||
text.AppendLine(Loc.GetString("forensic-scanner-interface-residues"));
|
||||
foreach (var residue in msg.Residues)
|
||||
|
||||
@@ -8,10 +8,12 @@ using Content.Shared.Alert;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.Drunk;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Forensics;
|
||||
using Content.Shared.HealthExaminable;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Popups;
|
||||
@@ -54,6 +56,7 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
SubscribeLocalEvent<BloodstreamComponent, ReactionAttemptEvent>(OnReactionAttempt);
|
||||
SubscribeLocalEvent<BloodstreamComponent, SolutionRelayEvent<ReactionAttemptEvent>>(OnReactionAttempt);
|
||||
SubscribeLocalEvent<BloodstreamComponent, RejuvenateEvent>(OnRejuvenate);
|
||||
SubscribeLocalEvent<BloodstreamComponent, GenerateDnaEvent>(OnDnaGenerated);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<BloodstreamComponent> ent, ref MapInitEvent args)
|
||||
@@ -183,8 +186,18 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
bloodSolution.MaxVolume = entity.Comp.BloodMaxVolume;
|
||||
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
|
||||
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)
|
||||
@@ -349,7 +362,7 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
}
|
||||
|
||||
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,
|
||||
// since we also wanna handle moving it to the temporary solution
|
||||
@@ -370,10 +383,7 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
tempSolution.AddSolution(temp, _prototypeManager);
|
||||
}
|
||||
|
||||
if (_puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, sound: false))
|
||||
{
|
||||
_forensicsSystem.TransferDna(puddleUid, uid, canDnaBeCleaned: false);
|
||||
}
|
||||
_puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, sound: false);
|
||||
|
||||
tempSolution.RemoveAllSolution();
|
||||
}
|
||||
@@ -436,10 +446,7 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
_solutionContainerSystem.RemoveAllSolution(component.TemporarySolution.Value);
|
||||
}
|
||||
|
||||
if (_puddleSystem.TrySpillAt(uid, tempSol, out var puddleUid))
|
||||
{
|
||||
_forensicsSystem.TransferDna(puddleUid, uid, canDnaBeCleaned: false);
|
||||
}
|
||||
_puddleSystem.TrySpillAt(uid, tempSol, out var puddleUid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -464,6 +471,40 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
component.BloodReagent = reagent;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
var reagent = _protoManager.Index<ReagentPrototype>(reagentQuantity.Reagent.Prototype);
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -21,10 +21,12 @@ public sealed partial class CleanDecalsReaction : ITileReaction
|
||||
[DataField]
|
||||
public FixedPoint2 CleanCost { get; private set; } = FixedPoint2.New(0.25f);
|
||||
|
||||
|
||||
public FixedPoint2 TileReact(TileRef tile,
|
||||
ReagentPrototype reagent,
|
||||
FixedPoint2 reactVolume,
|
||||
IEntityManager entityManager)
|
||||
IEntityManager entityManager,
|
||||
List<ReagentData>? data)
|
||||
{
|
||||
if (reactVolume <= CleanCost ||
|
||||
!entityManager.TryGetComponent<MapGridComponent>(tile.GridUid, out var grid) ||
|
||||
|
||||
@@ -34,7 +34,8 @@ public sealed partial class CleanTileReaction : ITileReaction
|
||||
FixedPoint2 ITileReaction.TileReact(TileRef tile,
|
||||
ReagentPrototype reagent,
|
||||
FixedPoint2 reactVolume,
|
||||
IEntityManager entityManager)
|
||||
IEntityManager entityManager
|
||||
, List<ReagentData>? data)
|
||||
{
|
||||
var entities = entityManager.System<EntityLookupSystem>().GetLocalEntitiesIntersecting(tile, 0f).ToArray();
|
||||
var puddleQuery = entityManager.GetEntityQuery<PuddleComponent>();
|
||||
|
||||
@@ -38,7 +38,8 @@ public sealed partial class CreateEntityTileReaction : ITileReaction
|
||||
public FixedPoint2 TileReact(TileRef tile,
|
||||
ReagentPrototype reagent,
|
||||
FixedPoint2 reactVolume,
|
||||
IEntityManager entityManager)
|
||||
IEntityManager entityManager,
|
||||
List<ReagentData>? data)
|
||||
{
|
||||
if (reactVolume >= Usage)
|
||||
{
|
||||
|
||||
@@ -17,7 +17,8 @@ namespace Content.Server.Chemistry.TileReactions
|
||||
public FixedPoint2 TileReact(TileRef tile,
|
||||
ReagentPrototype reagent,
|
||||
FixedPoint2 reactVolume,
|
||||
IEntityManager entityManager)
|
||||
IEntityManager entityManager,
|
||||
List<ReagentData>? data)
|
||||
{
|
||||
if (reactVolume <= FixedPoint2.Zero || tile.Tile.IsEmpty)
|
||||
return FixedPoint2.Zero;
|
||||
|
||||
@@ -16,7 +16,8 @@ namespace Content.Server.Chemistry.TileReactions
|
||||
public FixedPoint2 TileReact(TileRef tile,
|
||||
ReagentPrototype reagent,
|
||||
FixedPoint2 reactVolume,
|
||||
IEntityManager entityManager)
|
||||
IEntityManager entityManager,
|
||||
List<ReagentData>? data)
|
||||
{
|
||||
if (reactVolume <= FixedPoint2.Zero || tile.Tile.IsEmpty)
|
||||
return FixedPoint2.Zero;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Server.Maps;
|
||||
using Content.Server.Maps;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.FixedPoint;
|
||||
@@ -15,7 +15,8 @@ public sealed partial class PryTileReaction : ITileReaction
|
||||
public FixedPoint2 TileReact(TileRef tile,
|
||||
ReagentPrototype reagent,
|
||||
FixedPoint2 reactVolume,
|
||||
IEntityManager entityManager)
|
||||
IEntityManager entityManager,
|
||||
List<ReagentData>? data)
|
||||
{
|
||||
var sys = entityManager.System<TileSystem>();
|
||||
sys.PryTile(tile);
|
||||
|
||||
@@ -15,13 +15,14 @@ namespace Content.Server.Chemistry.TileReactions
|
||||
public FixedPoint2 TileReact(TileRef tile,
|
||||
ReagentPrototype reagent,
|
||||
FixedPoint2 reactVolume,
|
||||
IEntityManager entityManager)
|
||||
IEntityManager entityManager,
|
||||
List<ReagentData>? data)
|
||||
{
|
||||
var spillSystem = entityManager.System<PuddleSystem>();
|
||||
if (reactVolume < 5 || !spillSystem.TryGetPuddle(tile, out _))
|
||||
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
|
||||
: FixedPoint2.Zero;
|
||||
}
|
||||
|
||||
@@ -29,13 +29,14 @@ namespace Content.Server.Chemistry.TileReactions
|
||||
public FixedPoint2 TileReact(TileRef tile,
|
||||
ReagentPrototype reagent,
|
||||
FixedPoint2 reactVolume,
|
||||
IEntityManager entityManager)
|
||||
IEntityManager entityManager,
|
||||
List<ReagentData>? data)
|
||||
{
|
||||
if (reactVolume < 5)
|
||||
return FixedPoint2.Zero;
|
||||
|
||||
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);
|
||||
slippery.LaunchForwardsMultiplier = _launchForwardsMultiplier;
|
||||
|
||||
@@ -712,7 +712,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
|
||||
{
|
||||
var (reagent, quantity) = solution.Contents[i];
|
||||
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)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -315,7 +315,7 @@ public sealed class SmokeSystem : EntitySystem
|
||||
continue;
|
||||
|
||||
var reagent = _prototype.Index<ReagentPrototype>(reagentQuantity.Reagent.Prototype);
|
||||
reagent.ReactionTile(tile, reagentQuantity.Quantity, EntityManager);
|
||||
reagent.ReactionTile(tile, reagentQuantity.Quantity, EntityManager, reagentQuantity.Reagent.Data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{ }
|
||||
@@ -26,7 +26,13 @@ namespace Content.Server.Forensics
|
||||
/// DNA that the forensic scanner found from the <see cref="DNAComponent"/> on an entity.
|
||||
/// </summary>
|
||||
[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>
|
||||
/// Residue that the forensic scanner found from the <see cref="ForensicsComponent"/> on an entity.
|
||||
|
||||
@@ -3,16 +3,19 @@ using System.Text;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Fluids.Components;
|
||||
using Content.Shared.Forensics;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Paper;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
// todo: remove this stinky LINQy
|
||||
|
||||
namespace Content.Server.Forensics
|
||||
@@ -27,6 +30,9 @@ namespace Content.Server.Forensics
|
||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = 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()
|
||||
{
|
||||
@@ -46,7 +52,8 @@ namespace Content.Server.Forensics
|
||||
var state = new ForensicScannerBoundUserInterfaceState(
|
||||
component.Fingerprints,
|
||||
component.Fibers,
|
||||
component.DNAs,
|
||||
component.TouchDNAs,
|
||||
component.SolutionDNAs,
|
||||
component.Residues,
|
||||
component.LastScannedName,
|
||||
component.PrintCooldown,
|
||||
@@ -69,18 +76,25 @@ namespace Content.Server.Forensics
|
||||
{
|
||||
scanner.Fingerprints = new();
|
||||
scanner.Fibers = new();
|
||||
scanner.DNAs = new();
|
||||
scanner.TouchDNAs = new();
|
||||
scanner.Residues = new();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
scanner.Fingerprints = forensics.Fingerprints.ToList();
|
||||
scanner.Fibers = forensics.Fibers.ToList();
|
||||
scanner.DNAs = forensics.DNAs.ToList();
|
||||
scanner.TouchDNAs = forensics.DNAs.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;
|
||||
}
|
||||
|
||||
@@ -206,10 +220,17 @@ namespace Content.Server.Forensics
|
||||
}
|
||||
text.AppendLine();
|
||||
text.AppendLine(Loc.GetString("forensic-scanner-interface-dnas"));
|
||||
foreach (var dna in component.DNAs)
|
||||
foreach (var dna in component.TouchDNAs)
|
||||
{
|
||||
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(Loc.GetString("forensic-scanner-interface-residues"));
|
||||
foreach (var residue in component.Residues)
|
||||
@@ -232,7 +253,8 @@ namespace Content.Server.Forensics
|
||||
{
|
||||
component.Fingerprints = new();
|
||||
component.Fibers = new();
|
||||
component.DNAs = new();
|
||||
component.TouchDNAs = new();
|
||||
component.SolutionDNAs = new();
|
||||
component.LastScannedName = string.Empty;
|
||||
|
||||
UpdateUserInterface(uid, component);
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Server.Fluids.EntitySystems;
|
||||
using Content.Server.Forensics.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Fluids.Components;
|
||||
using Content.Shared.Forensics;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
@@ -23,20 +28,35 @@ namespace Content.Server.Forensics
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<FingerprintComponent, ContactInteractionEvent>(OnInteract);
|
||||
SubscribeLocalEvent<FingerprintComponent, MapInitEvent>(OnFingerprintInit);
|
||||
SubscribeLocalEvent<DnaComponent, MapInitEvent>(OnDNAInit);
|
||||
|
||||
SubscribeLocalEvent<DnaComponent, BeingGibbedEvent>(OnBeingGibbed);
|
||||
SubscribeLocalEvent<ForensicsComponent, 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);
|
||||
SubscribeLocalEvent<DnaSubstanceTraceComponent, SolutionContainerChangedEvent>(OnSolutionChanged);
|
||||
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)
|
||||
@@ -50,16 +70,27 @@ namespace Content.Server.Forensics
|
||||
}
|
||||
|
||||
private void OnDNAInit(EntityUid uid, DnaComponent component, MapInitEvent args)
|
||||
{
|
||||
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)
|
||||
{
|
||||
var partComp = EnsureComp<ForensicsComponent>(part);
|
||||
partComp.DNAs.Add(component.DNA);
|
||||
partComp.DNAs.Add(dna);
|
||||
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)
|
||||
{
|
||||
if (args.Handled || !args.CanReach || args.Target == null)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using Content.Server.Cuffs;
|
||||
using Content.Server.Cuffs;
|
||||
using Content.Server.Forensics;
|
||||
using Content.Server.Humanoid;
|
||||
using Content.Server.Implants.Components;
|
||||
using Content.Server.Store.Components;
|
||||
using Content.Server.Store.Systems;
|
||||
using Content.Shared.Cuffs.Components;
|
||||
using Content.Shared.Forensics;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Implants;
|
||||
using Content.Shared.Implants.Components;
|
||||
@@ -212,6 +213,9 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem
|
||||
if (TryComp<DnaComponent>(ent, out var dna))
|
||||
{
|
||||
dna.DNA = _forensicsSystem.GenerateDNA();
|
||||
|
||||
var ev = new GenerateDnaEvent { Owner = ent, DNA = dna.DNA };
|
||||
RaiseLocalEvent(ent, ref ev);
|
||||
}
|
||||
if (TryComp<FingerprintComponent>(ent, out var fingerprint))
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ using Content.Server.Forensics;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Stunnable;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
@@ -28,6 +29,7 @@ namespace Content.Server.Medical
|
||||
[Dependency] private readonly StunSystem _stun = default!;
|
||||
[Dependency] private readonly ThirstSystem _thirst = default!;
|
||||
[Dependency] private readonly ForensicsSystem _forensics = default!;
|
||||
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
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))
|
||||
|
||||
@@ -147,7 +147,7 @@ namespace Content.Shared.Chemistry.Components
|
||||
/// </summary>
|
||||
/// <param name="prototype">The prototype ID of the reagent to add.</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);
|
||||
}
|
||||
@@ -243,7 +243,7 @@ namespace Content.Shared.Chemistry.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool ContainsReagent(string reagentId, ReagentData? data)
|
||||
public bool ContainsReagent(string reagentId, List<ReagentData>? data)
|
||||
=> ContainsReagent(new(reagentId, data));
|
||||
|
||||
public bool TryGetReagent(ReagentId id, out ReagentQuantity quantity)
|
||||
@@ -404,7 +404,7 @@ namespace Content.Shared.Chemistry.Components
|
||||
/// </summary>
|
||||
/// <param name="proto">The prototype of the reagent to add.</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)
|
||||
UpdateHeatCapacity(protoMan);
|
||||
@@ -489,7 +489,7 @@ namespace Content.Shared.Chemistry.Components
|
||||
{
|
||||
var (reagent, curQuantity) = Contents[i];
|
||||
|
||||
if(reagent != toRemove.Reagent)
|
||||
if(reagent.Prototype != toRemove.Reagent.Prototype)
|
||||
continue;
|
||||
|
||||
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="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>
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -452,7 +452,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
||||
/// <param name="quantity">The amount of reagent to add.</param>
|
||||
/// <returns>If all the reagent could be added.</returns>
|
||||
[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);
|
||||
|
||||
/// <summary>
|
||||
@@ -464,7 +464,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
||||
/// <param name="quantity">The amount of reagent to add.</param>
|
||||
/// <param name="acceptedQuantity">The amount of reagent successfully added.</param>
|
||||
/// <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);
|
||||
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="quantity">The amount of reagent to remove.</param>
|
||||
/// <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));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Content.Shared.Chemistry.Reaction
|
||||
FixedPoint2 TileReact(TileRef tile,
|
||||
ReagentPrototype reagent,
|
||||
FixedPoint2 reactVolume,
|
||||
IEntityManager entityManager);
|
||||
IEntityManager entityManager,
|
||||
List<ReagentData>? data = null);
|
||||
}
|
||||
}
|
||||
|
||||
28
Content.Shared/Chemistry/Reagent/DNAData.cs
Normal file
28
Content.Shared/Chemistry/Reagent/DNAData.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using System.Linq;
|
||||
|
||||
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.
|
||||
/// </summary>
|
||||
[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;
|
||||
Data = data;
|
||||
Data = data ?? new();
|
||||
}
|
||||
|
||||
public ReagentId()
|
||||
{
|
||||
Prototype = default!;
|
||||
Data = new();
|
||||
}
|
||||
|
||||
public List<ReagentData> EnsureReagentData()
|
||||
{
|
||||
return (Data != null) ? Data : new List<ReagentData>();
|
||||
}
|
||||
|
||||
public bool Equals(ReagentId other)
|
||||
@@ -44,13 +51,13 @@ public partial struct ReagentId : IEquatable<ReagentId>
|
||||
if (other.Data == null)
|
||||
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 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)
|
||||
return false;
|
||||
@@ -73,12 +80,12 @@ public partial struct ReagentId : IEquatable<ReagentId>
|
||||
|
||||
public string ToString(FixedPoint2 quantity)
|
||||
{
|
||||
return Data?.ToString(Prototype, quantity) ?? $"{Prototype}:{quantity}";
|
||||
return $"{Prototype}:{quantity}";
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Data?.ToString(Prototype) ?? Prototype;
|
||||
return $"{Prototype}";
|
||||
}
|
||||
|
||||
public static bool operator ==(ReagentId left, ReagentId right)
|
||||
|
||||
@@ -143,7 +143,7 @@ namespace Content.Shared.Chemistry.Reagent
|
||||
[DataField]
|
||||
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;
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace Content.Shared.Chemistry.Reagent
|
||||
|
||||
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)
|
||||
throw new Exception("Removed more than we have!");
|
||||
|
||||
@@ -17,7 +17,7 @@ public partial struct ReagentQuantity : IEquatable<ReagentQuantity>
|
||||
[ViewVariables]
|
||||
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)
|
||||
{
|
||||
}
|
||||
@@ -37,7 +37,7 @@ public partial struct ReagentQuantity : IEquatable<ReagentQuantity>
|
||||
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;
|
||||
quantity = Quantity;
|
||||
|
||||
@@ -51,3 +51,20 @@ public record struct TransferDnaEvent()
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ namespace Content.Shared.Forensics
|
||||
{
|
||||
public readonly List<string> Fingerprints = 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 string LastScannedName = string.Empty;
|
||||
public readonly TimeSpan PrintCooldown = TimeSpan.Zero;
|
||||
@@ -16,7 +17,8 @@ namespace Content.Shared.Forensics
|
||||
public ForensicScannerBoundUserInterfaceState(
|
||||
List<string> fingerprints,
|
||||
List<string> fibers,
|
||||
List<string> dnas,
|
||||
List<string> touchDnas,
|
||||
List<string> solutionDnas,
|
||||
List<string> residues,
|
||||
string lastScannedName,
|
||||
TimeSpan printCooldown,
|
||||
@@ -24,7 +26,8 @@ namespace Content.Shared.Forensics
|
||||
{
|
||||
Fingerprints = fingerprints;
|
||||
Fibers = fibers;
|
||||
DNAs = dnas;
|
||||
TouchDNAs = touchDnas;
|
||||
SolutionDNAs = solutionDnas;
|
||||
Residues = residues;
|
||||
LastScannedName = lastScannedName;
|
||||
PrintCooldown = printCooldown;
|
||||
|
||||
@@ -23,6 +23,8 @@ forensic-scanner-verb-message = Perform a forensic scan
|
||||
forensic-pad-fingerprint-name = {$entity}'s fingerprints
|
||||
forensic-pad-gloves-name = fibers from {$entity}
|
||||
|
||||
forensics-dna-unknown = unknown DNA
|
||||
|
||||
forensics-verb-text = Remove evidence
|
||||
forensics-verb-message = Remove fingerprints and DNA residues from the object!
|
||||
forensics-cleaning = You begin cleaning the evidence off of {THE($target)}...
|
||||
|
||||
@@ -162,3 +162,6 @@
|
||||
solution: puddle
|
||||
- type: BadDrink
|
||||
- type: IgnoresFingerprints
|
||||
- type: Tag
|
||||
tags:
|
||||
- DNASolutionScannable
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#In future maybe add generic plastic scrap trash/debris
|
||||
- type: TrashOnSolutionEmpty
|
||||
solution: drink
|
||||
- type: DnaSubstanceTrace
|
||||
|
||||
- type: entity
|
||||
parent: DrinkCartonBaseFull
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
interfaces:
|
||||
enum.TransferAmountUiKey.Key:
|
||||
type: TransferAmountBoundUserInterface
|
||||
- type: DnaSubstanceTrace
|
||||
|
||||
- type: entity
|
||||
parent: DrinkBase
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
- type: PhysicalComposition
|
||||
materialComposition:
|
||||
Plastic: 100
|
||||
- type: DnaSubstanceTrace
|
||||
- type: PressurizedSolution
|
||||
solution: drink
|
||||
- type: Shakeable
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
- type: Tag
|
||||
tags:
|
||||
- DrinkCan
|
||||
- type: DnaSubstanceTrace
|
||||
|
||||
- type: entity
|
||||
parent: DrinkCanBaseFull
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
damage:
|
||||
types:
|
||||
Blunt: 0
|
||||
- type: DnaSubstanceTrace
|
||||
|
||||
- type: entity
|
||||
parent: DrinkBaseCup
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
- type: PhysicalComposition
|
||||
materialComposition:
|
||||
Steel: 50
|
||||
- type: DnaSubstanceTrace
|
||||
- type: ReactionMixer
|
||||
mixOnInteract: false
|
||||
reactionTypes:
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
materialComposition:
|
||||
Glass: 100
|
||||
- type: SpaceGarbage
|
||||
- type: DnaSubstanceTrace
|
||||
|
||||
- type: entity
|
||||
name: base empty bottle
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
- type: GuideHelp
|
||||
guides:
|
||||
- Janitorial
|
||||
- type: DnaSubstanceTrace
|
||||
|
||||
- type: entity
|
||||
parent: BaseItem
|
||||
@@ -109,6 +110,7 @@
|
||||
tags:
|
||||
- Mop
|
||||
- MopAdv
|
||||
- type: DnaSubstanceTrace
|
||||
|
||||
- type: entity
|
||||
name: wet floor sign
|
||||
@@ -332,3 +334,4 @@
|
||||
- type: CleansForensics
|
||||
- type: Fiber
|
||||
fiberColor: fibers-white
|
||||
- type: DnaSubstanceTrace
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
- type: Tag
|
||||
tags:
|
||||
- ChemDispensable
|
||||
- type: DnaSubstanceTrace
|
||||
|
||||
- type: entity
|
||||
parent: Jug
|
||||
|
||||
@@ -93,6 +93,7 @@
|
||||
transferForensics: true
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- type: DnaSubstanceTrace
|
||||
|
||||
- type: entity
|
||||
parent: BaseChemistryEmptyBottle
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
acts: [ "Destruction" ]
|
||||
- type: Spillable
|
||||
solution: beaker
|
||||
- type: DnaSubstanceTrace
|
||||
|
||||
- type: entity
|
||||
id: VestineChemistryVial
|
||||
|
||||
@@ -92,6 +92,7 @@
|
||||
Blunt: 5
|
||||
- type: StaticPrice
|
||||
price: 30
|
||||
- type: DnaSubstanceTrace
|
||||
|
||||
- type: entity
|
||||
parent: BaseItem
|
||||
@@ -153,6 +154,7 @@
|
||||
damageModifierSet: Glass
|
||||
- type: StaticPrice
|
||||
price: 30
|
||||
- type: DnaSubstanceTrace
|
||||
|
||||
- type: entity
|
||||
name: beaker
|
||||
@@ -303,6 +305,7 @@
|
||||
inHandsFillBaseName: -fill-
|
||||
- type: StaticPrice
|
||||
price: 40
|
||||
- type: DnaSubstanceTrace
|
||||
- type: Tag
|
||||
tags:
|
||||
- Dropper
|
||||
|
||||
@@ -67,3 +67,4 @@
|
||||
- type: PhysicalComposition
|
||||
materialComposition:
|
||||
Plastic: 50
|
||||
- type: DnaSubstanceTrace
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
- type: DnaSubstanceTrace
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
fix1:
|
||||
@@ -334,3 +335,4 @@
|
||||
- type: GuideHelp
|
||||
guides:
|
||||
- Janitorial
|
||||
- type: DnaSubstanceTrace
|
||||
|
||||
@@ -509,6 +509,9 @@
|
||||
- type: Tag
|
||||
id: DiscreteHealthAnalyzer #So construction recipes don't eat medical PDAs
|
||||
|
||||
- type: Tag
|
||||
id: DNASolutionScannable
|
||||
|
||||
- type: Tag
|
||||
id: DockArrivals
|
||||
|
||||
|
||||
Reference in New Issue
Block a user