Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,10 @@ private void ApplyFilter(Entity<ScentMarkerComponent> ent, SmellerComponent smel
return;

var visible = !IsPerceptionBlocked(smeller) &&
!(smeller.Perception == ScentPerception.Partial && ent.Comp.ContainedIn != null && !ent.Comp.WasDead) &&
!(smeller.Perception == ScentPerception.Partial && IsOutsideOwnEnclosure(ent, enclosure)) &&
(smeller.TrackedScentId == null || ent.Comp.ScentId == smeller.TrackedScentId);
!(smeller.Perception == ScentPerception.Partial && ent.Comp.WasCloaked) &&
!(smeller.Perception == ScentPerception.Partial && ent.Comp.ContainedIn != null && !ent.Comp.WasDead) &&
!(smeller.Perception == ScentPerception.Partial && IsOutsideOwnEnclosure(ent, enclosure)) &&
(smeller.TrackedScentId == null || ent.Comp.ScentId == smeller.TrackedScentId);
_sprite.SetVisible((ent.Owner, sprite), visible);
}

Expand Down
66 changes: 36 additions & 30 deletions Content.Server/_Starlight/Scent/Systems/ScentSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
using Content.Shared.Nutrition.EntitySystems;
using Content.Shared.Popups;
using Content.Shared.Storage.Components;
using Content.Shared.Stealth;
using Content.Shared.Stealth.Components;
using Content.Shared.Tag;
using Content.Shared.Verbs;
using Content.Shared.Zombies;
Expand All @@ -36,20 +38,21 @@

namespace Content.Server._Starlight.Scent.Systems;

public sealed class ScentSystem : SharedScentSystem
public sealed partial class ScentSystem : SharedScentSystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly SharedEyeSystem _eye = default!;
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly TagSystem _tags = default!;
[Dependency] private IRobustRandom _random = default!;
[Dependency] private IGameTiming _timing = default!;
[Dependency] private SharedEyeSystem _eye = default!;
[Dependency] private SharedUserInterfaceSystem _ui = default!;
[Dependency] private DoAfterSystem _doAfterSystem = default!;
[Dependency] private PopupSystem _popup = default!;
[Dependency] private SharedAudioSystem _audio = default!;
[Dependency] private SharedTransformSystem _transform = default!;
[Dependency] private IPrototypeManager _prototype = default!;
[Dependency] private AtmosphereSystem _atmosphere = default!;
[Dependency] private InventorySystem _inventory = default!;
[Dependency] private TagSystem _tags = default!;
[Dependency] private SharedStealthSystem _stealth = default!;

private const string ScentMarkerPrototype = "ScentMarker";
private const int ScentIdByteLength = 8;
Expand Down Expand Up @@ -170,7 +173,7 @@ private void OnSniffMenuClosed(Entity<SmellerComponent> ent, ref BoundUIClosedEv

private void OnSmellerMove(Entity<SmellerComponent> ent, ref MoveEvent args)
{
if (ent.Comp.SniffTarget is not { } target || !TryComp<TransformComponent>(target, out var targetXform))
if (ent.Comp.SniffTarget is not { } target || !TryComp(target, out TransformComponent? targetXform))
return;

if (_transform.InRange(args.NewPosition, targetXform.Coordinates, ent.Comp.SniffRange))
Expand All @@ -181,17 +184,15 @@ private void OnSmellerMove(Entity<SmellerComponent> ent, ref MoveEvent args)
}

// Zombies shouldn't be able to hunt survivors by scent.
private void OnSmellerZombified(Entity<SmellerComponent> ent, ref EntityZombifiedEvent args)
{
private void OnSmellerZombified(Entity<SmellerComponent> ent, ref EntityZombifiedEvent args) =>
RemComp<SmellerComponent>(ent.Owner);
}

private void OnTrackMessage(EntityUid uid, SmellerComponent component, ScentSniffTrackMessage args)
{
if (component.SniffTarget is not { } target || !Exists(target))
return;

if (!TryComp<TransformComponent>(uid, out var xform) || !TryComp<TransformComponent>(target, out var targetXform))
if (!TryComp(uid, out TransformComponent? xform) || !TryComp(target, out TransformComponent? targetXform))
return;

if (!_transform.InRange(xform.Coordinates, targetXform.Coordinates, component.SniffRange))
Expand Down Expand Up @@ -420,7 +421,7 @@ public override void Update(float frameTime)

scent.NextEmitTime = now + RollEmitDelay(scent);

if (TryComp<TransformComponent>(uid, out var xform))
if (TryComp(uid, out TransformComponent? xform))
EmitScent((uid, scent, xform), scentId);
}
}
Expand Down Expand Up @@ -466,6 +467,7 @@ private void EmitScent(Entity<ScentComponent, TransformComponent> ent, string sc
markerComp.TotalDuration = decayTime;
markerComp.ContainedIn = GetAirtightContainer(xform);
markerComp.WasDead = MobState.IsDead(uid);
markerComp.WasCloaked = IsCloaked(uid);
Dirty(marker, markerComp);

var despawn = Comp<TimedDespawnComponent>(marker);
Expand Down Expand Up @@ -497,7 +499,7 @@ private bool IsSealed(EntityUid uid)
if (TryComp<EntityStorageComponent>(parent, out var storage) && storage.Airtight)
return parent;

if (!TryComp<TransformComponent>(parent, out var parentXform))
if (!TryComp(parent, out TransformComponent? parentXform))
break;

parent = parentXform.ParentUid;
Expand All @@ -506,17 +508,20 @@ private bool IsSealed(EntityUid uid)
return null;
}

// Same threshold the game already uses to decide an entity is too hidden to examine
// (StealthComponent.ExamineThreshold), reused here instead of picking a new cutoff.
private bool IsCloaked(EntityUid uid) =>
TryComp<StealthComponent>(uid, out var stealth) &&
stealth.Enabled &&
_stealth.GetVisibility(uid, stealth) <= stealth.ExamineThreshold;

// Suppressed everywhere in the gas pipe network except at a terminus (vent/scrubber), where
// the scent is genuinely reaching open air.
private bool IsHiddenVentCrawl(EntityUid uid)
{
if (!TryComp<BeingVentCrawlComponent>(uid, out var ventCrawl))
return false;

return !TryComp<VentCrawlHolderComponent>(ventCrawl.Holder, out var holder) ||
holder.CurrentTube is not { } tube ||
!HasComp<VentCrawlEntryComponent>(tube);
}
private bool IsHiddenVentCrawl(EntityUid uid) =>
TryComp<BeingVentCrawlComponent>(uid, out var ventCrawl) &&
(!TryComp<VentCrawlHolderComponent>(ventCrawl.Holder, out var holder) ||
holder.CurrentTube is not { } tube ||
!HasComp<VentCrawlEntryComponent>(tube));

// Only merges into our own chain tail, never any other nearby marker. Revisiting an old spot
// would otherwise rewrite the trail's visit order.
Expand All @@ -526,7 +531,7 @@ private bool TryMergeIntoExisting(Entity<ScentComponent, TransformComponent> ent

if (scent.LastMarkerEntity is not { } tail ||
!TryComp<ScentMarkerComponent>(tail, out var marker) ||
!TryComp<TransformComponent>(tail, out var tailXform))
!TryComp(tail, out TransformComponent? tailXform))
{
return false;
}
Expand All @@ -541,6 +546,7 @@ private bool TryMergeIntoExisting(Entity<ScentComponent, TransformComponent> ent
marker.TotalDuration = decayTime;
marker.ContainedIn = GetAirtightContainer(xform);
marker.WasDead = MobState.IsDead(uid);
marker.WasCloaked = IsCloaked(uid);
Dirty(tail, marker);

if (TryComp<TimedDespawnComponent>(tail, out var despawn))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public sealed partial class ScentMarkerComponent : Component
public TimeSpan ExpiresAt;

/// <summary>
// How pooled this marker is, 0-1. Maps to alpha/scale client-side.
/// How pooled this marker is, 0-1. Maps to alpha/scale client-side.
/// </summary>
/// </summary>
[DataField, AutoNetworkedField]
public float Strength;
Expand All @@ -44,4 +45,10 @@ public sealed partial class ScentMarkerComponent : Component
/// </summary>
[DataField, AutoNetworkedField]
public bool WasDead;
/// <summary>
/// Whether the emitter was cloaked (StealthComponent, hidden past its ExamineThreshold) at
/// the moment of this emission.
/// </summary>
[DataField, AutoNetworkedField]
public bool WasCloaked;
}
Loading