mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-26 18:17:12 +03:00
 <!-- This is a semi-strict format, you can add/remove sections as needed but the order/format should be kept the same Remove these comments before submitting --> # Description <!-- Explain this PR in as much detail as applicable Some example prompts to consider: How might this affect the game? The codebase? What might be some alternatives to this? How/Who does this benefit/hurt [the game/codebase]? --> This PR is the rework regarding the unique feature for vulpkanins, this is mostly due to the "FORCED" Issue by VM: https://github.com/Simple-Station/Einstein-Engines/issues/711 This PR will mostly add the new features that will mostly make Vulpkanins unique. For Vulpkanin Stats changed please check this PR: https://github.com/Simple-Station/Einstein-Engines/pull/713 - Flash Damge: Flashable has 2 new variables "EyeDamageChance" (Float) and "EyeDamage" (int), those are default to 0 but if changed could give a chance from 0 to 1 to give EyeDamage from the EyeDamage Value, this is not fixed to vulpkanin and can be added to anything with the "Flashable" Component. - ScentTracker: Add a new Forensics type "Scent", scent will spread on everything you wear and only the ent with the "ScentTrackerSystem" can track a scent, tracking a scent will leave an effect on those who has or the item with the scent, scent can be cleaned away with soap or you can compleatly generate a new scent of a person by cleaning yourself, note: someone with a scent does not mean his the one making that scent, they may just have an item with the scent in their bag! - Vulpkanins Screams: I have 5 Fox Screams that need to be edited and need to be added in-game for vulpkanins with a lisence, just need to have the time to do it and this PR seem the perfect place for it. --- # TODO <!-- A list of everything you have to do before this PR is "complete" You probably won't have to complete everything before merging but it's good to leave future references --> - [x] Flash Damage - [x] Scent System - [x] ScentTracker System - [x] Vulpkanin Screams --- <!-- This is default collapsed, readers click to expand it and see all your media The PR media section can get very large at times, so this is a good way to keep it clean The title is written using HTML tags The title must be within the <summary> tags or you won't see it --> <details><summary><h1>Media</h1></summary> <p>     </p> </details> --- # Changelog <!-- You can add an author after the `🆑` to change the name that appears in the changelog (ex: `🆑 Death`) Leaving it blank will default to your GitHub display name This includes all available types for the changelog --> 🆑 FoxxoTrystan - add: Forensics Scent Type and Vulpkanins can now track scents, better keep yourself clean! - tweak: Vulpkanins eyes are sensetive, please dont flash them with lights as this could damage them. - add: Vulpkanins now has their own screams! --------- Signed-off-by: FoxxoTrystan <45297731+FoxxoTrystan@users.noreply.github.com> Co-authored-by: VMSolidus <evilexecutive@gmail.com> Co-authored-by: Danger Revolution! <142105406+DangerRevolution@users.noreply.github.com>
323 lines
13 KiB
C#
323 lines
13 KiB
C#
using Content.Server.Body.Components;
|
|
using Content.Server.DoAfter;
|
|
using Content.Server.Fluids.EntitySystems;
|
|
using Content.Server.Forensics.Components;
|
|
using Content.Server.Popups;
|
|
using Content.Shared.Chemistry.Components;
|
|
using Content.Shared.DoAfter;
|
|
using Content.Shared.Forensics;
|
|
using Content.Shared.Interaction;
|
|
using Content.Shared.Interaction.Events;
|
|
using Content.Shared.Inventory;
|
|
using Content.Shared.Weapons.Melee.Events;
|
|
using Robust.Shared.Random;
|
|
using Content.Shared.Inventory.Events;
|
|
|
|
namespace Content.Server.Forensics
|
|
{
|
|
public sealed class ForensicsSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly IRobustRandom _random = default!;
|
|
[Dependency] private readonly InventorySystem _inventory = default!;
|
|
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
|
public override void Initialize()
|
|
{
|
|
SubscribeLocalEvent<FingerprintComponent, ContactInteractionEvent>(OnInteract);
|
|
SubscribeLocalEvent<ScentComponent, DidEquipEvent>(OnEquip);
|
|
SubscribeLocalEvent<FiberComponent, MapInitEvent>(OnFiberInit);
|
|
SubscribeLocalEvent<FingerprintComponent, MapInitEvent>(OnFingerprintInit);
|
|
SubscribeLocalEvent<DnaComponent, MapInitEvent>(OnDNAInit);
|
|
SubscribeLocalEvent<ScentComponent, MapInitEvent>(OnScentInit);
|
|
|
|
SubscribeLocalEvent<DnaComponent, BeingGibbedEvent>(OnBeingGibbed);
|
|
SubscribeLocalEvent<ForensicsComponent, MeleeHitEvent>(OnMeleeHit);
|
|
SubscribeLocalEvent<ForensicsComponent, GotRehydratedEvent>(OnRehydrated);
|
|
SubscribeLocalEvent<CleansForensicsComponent, AfterInteractEvent>(OnAfterInteract, after: new[] { typeof(AbsorbentSystem) });
|
|
SubscribeLocalEvent<ForensicsComponent, CleanForensicsDoAfterEvent>(OnCleanForensicsDoAfter);
|
|
SubscribeLocalEvent<DnaComponent, TransferDnaEvent>(OnTransferDnaEvent);
|
|
}
|
|
|
|
private void OnInteract(EntityUid uid, FingerprintComponent component, ContactInteractionEvent args)
|
|
{
|
|
ApplyEvidence(uid, args.Other);
|
|
}
|
|
|
|
private void OnEquip(EntityUid uid, ScentComponent component, DidEquipEvent args)
|
|
{
|
|
ApplyScent(uid, args.Equipment);
|
|
}
|
|
|
|
private void OnFiberInit(EntityUid uid, FiberComponent component, MapInitEvent args)
|
|
{
|
|
component.Fiberprint = GenerateFingerprint(length: 7);
|
|
}
|
|
|
|
private void OnFingerprintInit(EntityUid uid, FingerprintComponent component, MapInitEvent args)
|
|
{
|
|
component.Fingerprint = GenerateFingerprint();
|
|
}
|
|
|
|
private void OnDNAInit(EntityUid uid, DnaComponent component, MapInitEvent args)
|
|
{
|
|
component.DNA = GenerateDNA();
|
|
|
|
}
|
|
|
|
private void OnScentInit(EntityUid uid, ScentComponent component, MapInitEvent args)
|
|
{
|
|
component.Scent = GenerateFingerprint(length: 5);
|
|
|
|
var updatecomp = EnsureComp<ForensicsComponent>(uid);
|
|
updatecomp.Scent = component.Scent;
|
|
|
|
Dirty(uid, updatecomp);
|
|
}
|
|
|
|
private void OnBeingGibbed(EntityUid uid, DnaComponent component, BeingGibbedEvent args)
|
|
{
|
|
foreach (EntityUid part in args.GibbedParts)
|
|
{
|
|
var partComp = EnsureComp<ForensicsComponent>(part);
|
|
partComp.DNAs.Add(component.DNA);
|
|
partComp.CanDnaBeCleaned = false;
|
|
Dirty(part, partComp);
|
|
}
|
|
}
|
|
|
|
private void OnMeleeHit(EntityUid uid, ForensicsComponent component, MeleeHitEvent args)
|
|
{
|
|
if ((args.BaseDamage.DamageDict.TryGetValue("Blunt", out var bluntDamage) && bluntDamage.Value > 0) ||
|
|
(args.BaseDamage.DamageDict.TryGetValue("Slash", out var slashDamage) && slashDamage.Value > 0) ||
|
|
(args.BaseDamage.DamageDict.TryGetValue("Piercing", out var pierceDamage) && pierceDamage.Value > 0))
|
|
{
|
|
foreach (EntityUid hitEntity in args.HitEntities)
|
|
{
|
|
if (TryComp<DnaComponent>(hitEntity, out var hitEntityComp))
|
|
component.DNAs.Add(hitEntityComp.DNA);
|
|
}
|
|
}
|
|
Dirty(uid, component);
|
|
}
|
|
|
|
private void OnRehydrated(Entity<ForensicsComponent> ent, ref GotRehydratedEvent args)
|
|
{
|
|
CopyForensicsFrom(ent.Comp, args.Target);
|
|
Dirty(args.Target, ent.Comp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copy forensic information from a source entity to a destination.
|
|
/// Existing forensic information on the target is still kept.
|
|
/// </summary>
|
|
public void CopyForensicsFrom(ForensicsComponent src, EntityUid target)
|
|
{
|
|
var dest = EnsureComp<ForensicsComponent>(target);
|
|
foreach (var dna in src.DNAs)
|
|
{
|
|
dest.DNAs.Add(dna);
|
|
}
|
|
|
|
foreach (var fiber in src.Fibers)
|
|
{
|
|
dest.Fibers.Add(fiber);
|
|
}
|
|
|
|
foreach (var print in src.Fingerprints)
|
|
{
|
|
dest.Fingerprints.Add(print);
|
|
}
|
|
}
|
|
|
|
private void OnAfterInteract(EntityUid uid, CleansForensicsComponent component, AfterInteractEvent args)
|
|
{
|
|
if (args.Handled)
|
|
return;
|
|
|
|
if (TryComp<ForensicsComponent>(args.Target, out var forensicsComp)
|
|
&& forensicsComp.DNAs.Count > 0 && forensicsComp.CanDnaBeCleaned
|
|
&& forensicsComp.Fingerprints.Count + forensicsComp.Fibers.Count > 0
|
|
&& forensicsComp.Scent != string.Empty)
|
|
{
|
|
var cleanDelay = component.CleanDelay;
|
|
if (HasComp<ScentComponent>(args.Target))
|
|
cleanDelay += 30;
|
|
|
|
var doAfterArgs = new DoAfterArgs(EntityManager, args.User, cleanDelay, new CleanForensicsDoAfterEvent(), uid, target: args.Target, used: args.Used)
|
|
{
|
|
BreakOnHandChange = true,
|
|
NeedHand = true,
|
|
BreakOnDamage = true,
|
|
BreakOnTargetMove = true,
|
|
MovementThreshold = 0.01f,
|
|
DistanceThreshold = forensicsComp.CleanDistance,
|
|
};
|
|
|
|
_doAfterSystem.TryStartDoAfter(doAfterArgs);
|
|
_popupSystem.PopupEntity(Loc.GetString("forensics-cleaning", ("target", args.Target)), args.User, args.User);
|
|
|
|
args.Handled = true;
|
|
return;
|
|
}
|
|
|
|
if (TryComp<ScentComponent>(args.Target, out var scentComp))
|
|
{
|
|
var cleanDelay = component.CleanDelay + 30;
|
|
var doAfterArgs = new DoAfterArgs(EntityManager, args.User, cleanDelay, new CleanForensicsDoAfterEvent(), uid, target: args.Target, used: args.Used)
|
|
{
|
|
BreakOnHandChange = true,
|
|
NeedHand = true,
|
|
BreakOnDamage = true,
|
|
BreakOnTargetMove = true,
|
|
MovementThreshold = 0.01f,
|
|
DistanceThreshold = 1.5f,
|
|
};
|
|
|
|
_doAfterSystem.TryStartDoAfter(doAfterArgs);
|
|
_popupSystem.PopupEntity(Loc.GetString("forensics-cleaning", ("target", args.Target)), args.User, args.User);
|
|
|
|
args.Handled = true;
|
|
}
|
|
}
|
|
|
|
private void OnCleanForensicsDoAfter(EntityUid uid, ForensicsComponent component, CleanForensicsDoAfterEvent args)
|
|
{
|
|
if (args.Handled || args.Cancelled || args.Args.Target == null)
|
|
return;
|
|
|
|
if (!TryComp<ForensicsComponent>(args.Target, out var targetComp))
|
|
return;
|
|
|
|
targetComp.Fibers = new();
|
|
targetComp.Fingerprints = new();
|
|
targetComp.Scent = String.Empty;
|
|
|
|
if (targetComp.CanDnaBeCleaned)
|
|
targetComp.DNAs = new();
|
|
|
|
// leave behind evidence it was cleaned
|
|
if (TryComp<FiberComponent>(args.Used, out var fiber))
|
|
targetComp.Fibers.Add(string.IsNullOrEmpty(fiber.FiberColor) ? Loc.GetString("forensic-fibers", ("material", fiber.FiberMaterial)) : Loc.GetString("forensic-fibers-colored", ("color", fiber.FiberColor), ("material", fiber.FiberMaterial)));
|
|
|
|
if (TryComp<ResidueComponent>(args.Used, out var residue))
|
|
targetComp.Residues.Add(string.IsNullOrEmpty(residue.ResidueColor) ? Loc.GetString("forensic-residue", ("adjective", residue.ResidueAdjective)) : Loc.GetString("forensic-residue-colored", ("color", residue.ResidueColor), ("adjective", residue.ResidueAdjective)));
|
|
|
|
// If the ent has a Scent Component, we compleatly generate a new one and apply the new scent to all currently weared items.
|
|
if (TryComp<ScentComponent>(args.Target, out var scentComp))
|
|
{
|
|
var generatedscent = GenerateFingerprint(length: 5);
|
|
scentComp.Scent = generatedscent;
|
|
targetComp.Scent = generatedscent;
|
|
|
|
if (args.Target is { Valid: true } target
|
|
&& _inventory.TryGetSlots(target, out var slotDefinitions))
|
|
foreach (var slot in slotDefinitions)
|
|
{
|
|
if (!_inventory.TryGetSlotEntity(target, slot.Name, out var slotEnt))
|
|
continue;
|
|
|
|
EnsureComp<ForensicsComponent>(slotEnt.Value, out var recipientComp);
|
|
recipientComp.Scent = generatedscent;
|
|
|
|
Dirty(slotEnt.Value, recipientComp);
|
|
}
|
|
}
|
|
|
|
if (args.Target is { Valid: true } targetuid)
|
|
Dirty(targetuid, targetComp);
|
|
}
|
|
|
|
public string GenerateFingerprint(int length = 16)
|
|
{
|
|
var fingerprint = new byte[Math.Clamp(length, 0, 255)];
|
|
_random.NextBytes(fingerprint);
|
|
return Convert.ToHexString(fingerprint);
|
|
}
|
|
|
|
public string GenerateDNA()
|
|
{
|
|
var letters = new[] { "A", "C", "G", "T" };
|
|
var DNA = string.Empty;
|
|
|
|
for (var i = 0; i < 16; i++)
|
|
{
|
|
DNA += letters[_random.Next(letters.Length)];
|
|
}
|
|
|
|
return DNA;
|
|
}
|
|
|
|
private void ApplyEvidence(EntityUid user, EntityUid target)
|
|
{
|
|
if (HasComp<IgnoresFingerprintsComponent>(target))
|
|
return;
|
|
|
|
var component = EnsureComp<ForensicsComponent>(target);
|
|
if (_inventory.TryGetSlotEntity(user, "gloves", out var gloves))
|
|
{
|
|
if (TryComp<FiberComponent>(gloves, out var fiber) && !string.IsNullOrEmpty(fiber.FiberMaterial))
|
|
{
|
|
var fiberLocale = string.IsNullOrEmpty(fiber.FiberColor)
|
|
? Loc.GetString("forensic-fibers", ("material", fiber.FiberMaterial))
|
|
: Loc.GetString("forensic-fibers-colored", ("color", fiber.FiberColor), ("material", fiber.FiberMaterial));
|
|
component.Fibers.Add(fiberLocale + " ; " + fiber.Fiberprint);
|
|
}
|
|
|
|
if (HasComp<FingerprintMaskComponent>(gloves))
|
|
{
|
|
Dirty(target, component);
|
|
return;
|
|
}
|
|
}
|
|
if (TryComp<FingerprintComponent>(user, out var fingerprint))
|
|
{
|
|
component.Fingerprints.Add(fingerprint.Fingerprint ?? "");
|
|
Dirty(target, component);
|
|
}
|
|
}
|
|
|
|
private void ApplyScent(EntityUid user, EntityUid target)
|
|
{
|
|
if (HasComp<ScentComponent>(target))
|
|
return;
|
|
|
|
var component = EnsureComp<ForensicsComponent>(target);
|
|
if (TryComp<ScentComponent>(user, out var scent))
|
|
component.Scent = scent.Scent;
|
|
|
|
Dirty(target, component);
|
|
}
|
|
|
|
private void OnTransferDnaEvent(EntityUid uid, DnaComponent component, ref TransferDnaEvent args)
|
|
{
|
|
var recipientComp = EnsureComp<ForensicsComponent>(args.Recipient);
|
|
recipientComp.DNAs.Add(component.DNA);
|
|
recipientComp.CanDnaBeCleaned = args.CanDnaBeCleaned;
|
|
|
|
Dirty(args.Recipient, recipientComp);
|
|
}
|
|
|
|
#region Public API
|
|
|
|
/// <summary>
|
|
/// Transfer DNA from one entity onto the forensics of another
|
|
/// </summary>
|
|
/// <param name="recipient">The entity receiving the DNA</param>
|
|
/// <param name="donor">The entity applying its DNA</param>
|
|
/// <param name="canDnaBeCleaned">If this DNA be cleaned off of the recipient. e.g. cleaning a knife vs cleaning a puddle of blood</param>
|
|
public void TransferDna(EntityUid recipient, EntityUid donor, bool canDnaBeCleaned = true)
|
|
{
|
|
if (TryComp<DnaComponent>(donor, out var donorComp))
|
|
{
|
|
EnsureComp<ForensicsComponent>(recipient, out var recipientComp);
|
|
recipientComp.DNAs.Add(donorComp.DNA);
|
|
recipientComp.CanDnaBeCleaned = canDnaBeCleaned;
|
|
|
|
Dirty(recipient, recipientComp);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|