Move pipe visualizers to systems. (#6565)
This commit is contained in:
11
Content.Shared/Atmos/Components/PipeAppearanceComponent.cs
Normal file
11
Content.Shared/Atmos/Components/PipeAppearanceComponent.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Content.Shared.Atmos.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class PipeAppearanceComponent : Component
|
||||
{
|
||||
[DataField("rsi")]
|
||||
public string RsiPath = "Structures/Piping/Atmospherics/pipe.rsi";
|
||||
|
||||
[DataField("baseState")]
|
||||
public string BaseState = "pipeConnector";
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Maps;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
@@ -11,26 +10,11 @@ namespace Content.Shared.SubFloor
|
||||
/// Entity system backing <see cref="SubFloorHideComponent"/>.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class SubFloorHideSystem : EntitySystem
|
||||
public abstract class SharedSubFloorHideSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
||||
[Dependency] private readonly SharedTrayScannerSystem _trayScannerSystem = default!;
|
||||
|
||||
private bool _showAll;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool ShowAll
|
||||
{
|
||||
get => _showAll;
|
||||
set
|
||||
{
|
||||
if (_showAll == value) return;
|
||||
_showAll = value;
|
||||
|
||||
UpdateAll();
|
||||
}
|
||||
}
|
||||
[Dependency] private readonly TrayScannerSystem _trayScannerSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -42,7 +26,6 @@ namespace Content.Shared.SubFloor
|
||||
SubscribeLocalEvent<SubFloorHideComponent, ComponentStartup>(OnSubFloorStarted);
|
||||
SubscribeLocalEvent<SubFloorHideComponent, ComponentShutdown>(OnSubFloorTerminating);
|
||||
SubscribeLocalEvent<SubFloorHideComponent, AnchorStateChangedEvent>(HandleAnchorChanged);
|
||||
SubscribeLocalEvent<SubFloorHideComponent, ComponentHandleState>(HandleComponentState);
|
||||
SubscribeLocalEvent<SubFloorHideComponent, InteractUsingEvent>(OnInteractionAttempt);
|
||||
}
|
||||
|
||||
@@ -54,13 +37,6 @@ namespace Content.Shared.SubFloor
|
||||
_mapManager.TileChanged -= MapManagerOnTileChanged;
|
||||
}
|
||||
|
||||
public void SetEnabled(SubFloorHideComponent subFloor, bool enabled)
|
||||
{
|
||||
subFloor.Enabled = enabled;
|
||||
Dirty(subFloor);
|
||||
UpdateAppearance(subFloor.Owner);
|
||||
}
|
||||
|
||||
private void OnInteractionAttempt(EntityUid uid, SubFloorHideComponent component, InteractUsingEvent args)
|
||||
{
|
||||
// TODO make this use an interact attempt event or something. Handling an InteractUsing is not going to work in general.
|
||||
@@ -103,17 +79,14 @@ namespace Content.Shared.SubFloor
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleComponentState(EntityUid uid, SubFloorHideComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not SubFloorHideComponentState state)
|
||||
return;
|
||||
|
||||
component.Enabled = state.Enabled;
|
||||
UpdateAppearance(uid, component);
|
||||
}
|
||||
|
||||
private void MapManagerOnTileChanged(object? sender, TileChangedEventArgs e)
|
||||
{
|
||||
if (e.OldTile.IsEmpty)
|
||||
return; // Nothing is anchored here anyways.
|
||||
|
||||
if (e.NewTile.Tile.IsEmpty)
|
||||
return; // Anything that was here will be unanchored anyways.
|
||||
|
||||
UpdateTile(_mapManager.GetGrid(e.NewTile.GridIndex), e.NewTile.GridIndices);
|
||||
}
|
||||
|
||||
@@ -138,7 +111,6 @@ namespace Content.Shared.SubFloor
|
||||
else
|
||||
component.IsUnderCover = false;
|
||||
|
||||
// Update normally.
|
||||
UpdateAppearance(uid, component);
|
||||
}
|
||||
|
||||
@@ -149,14 +121,6 @@ namespace Content.Shared.SubFloor
|
||||
return !tileDef.IsSubFloor;
|
||||
}
|
||||
|
||||
private void UpdateAll()
|
||||
{
|
||||
foreach (var comp in EntityManager.EntityQuery<SubFloorHideComponent>(true))
|
||||
{
|
||||
UpdateAppearance(comp.Owner, comp);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTile(IMapGrid grid, Vector2i position)
|
||||
{
|
||||
var covered = HasFloorCover(grid, position);
|
||||
@@ -177,7 +141,7 @@ namespace Content.Shared.SubFloor
|
||||
/// <summary>
|
||||
/// This function is used by T-Ray scanners or other sub-floor revealing entities to toggle visibility.
|
||||
/// </summary>
|
||||
public void SetEntitiesRevealed(IEnumerable<EntityUid> entities, EntityUid revealer, bool visible, IEnumerable<object>? appearanceKeys = null)
|
||||
public void SetEntitiesRevealed(IEnumerable<EntityUid> entities, EntityUid revealer, bool visible)
|
||||
{
|
||||
foreach (var uid in entities)
|
||||
{
|
||||
@@ -188,77 +152,40 @@ namespace Content.Shared.SubFloor
|
||||
/// <summary>
|
||||
/// This function is used by T-Ray scanners or other sub-floor revealing entities to toggle visibility.
|
||||
/// </summary>
|
||||
public void SetEntityRevealed(EntityUid uid, EntityUid revealer, bool visible,
|
||||
SubFloorHideComponent? hideComp = null,
|
||||
IEnumerable<object>? appearanceKeys = null)
|
||||
public void SetEntityRevealed(EntityUid uid, EntityUid revealer, bool visible, SubFloorHideComponent? hideComp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref hideComp))
|
||||
if (!Resolve(uid, ref hideComp, false))
|
||||
return;
|
||||
|
||||
if (visible)
|
||||
{
|
||||
if (hideComp.RevealedBy.Add(revealer) && hideComp.RevealedBy.Count == 1)
|
||||
UpdateAppearance(uid, hideComp, appearanceKeys);
|
||||
UpdateAppearance(uid, hideComp);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (hideComp.RevealedBy.Remove(revealer) && hideComp.RevealedBy.Count == 0)
|
||||
UpdateAppearance(uid, hideComp, appearanceKeys);
|
||||
UpdateAppearance(uid, hideComp);
|
||||
}
|
||||
|
||||
public void UpdateAppearance(EntityUid uid, SubFloorHideComponent? hideComp = null, IEnumerable<object>? appearanceKeys = null)
|
||||
public void UpdateAppearance(
|
||||
EntityUid uid,
|
||||
SubFloorHideComponent? hideComp = null,
|
||||
AppearanceComponent? appearance = null)
|
||||
{
|
||||
if (!Resolve(uid, ref hideComp))
|
||||
if (!Resolve(uid, ref hideComp, ref appearance, false))
|
||||
return;
|
||||
|
||||
var revealedWithoutEntity = ShowAll || !hideComp.IsUnderCover;
|
||||
var revealed = revealedWithoutEntity || hideComp.RevealedBy.Count != 0;
|
||||
|
||||
// if there are no keys given,
|
||||
// or if the subfloor is already revealed,
|
||||
// set the keys to the default:
|
||||
//
|
||||
// the reason why it's set to default when the subfloor is
|
||||
// revealed without an entity is because the appearance keys
|
||||
// should only apply if the visualizer is underneath a subfloor
|
||||
if (appearanceKeys == null || revealedWithoutEntity) appearanceKeys = _defaultVisualizerKeys;
|
||||
|
||||
ShowSubfloorSprite(uid, revealed, appearanceKeys);
|
||||
appearance.SetData(SubFloorVisuals.Covered, hideComp.IsUnderCover);
|
||||
appearance.SetData(SubFloorVisuals.ScannerRevealed, hideComp.RevealedBy.Count != 0);
|
||||
}
|
||||
|
||||
private void ShowSubfloorSprite(EntityUid uid, bool revealed, IEnumerable<object> appearanceKeys)
|
||||
{
|
||||
// Show sprite
|
||||
if (EntityManager.TryGetComponent(uid, out SharedSpriteComponent? spriteComponent))
|
||||
{
|
||||
spriteComponent.Visible = revealed;
|
||||
}
|
||||
|
||||
// Set an appearance data value so visualizers can use this as needed.
|
||||
if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent))
|
||||
{
|
||||
foreach (var key in appearanceKeys)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case Enum enumKey:
|
||||
appearanceComponent.SetData(enumKey, revealed);
|
||||
break;
|
||||
case string stringKey:
|
||||
appearanceComponent.SetData(stringKey, revealed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<object> _defaultVisualizerKeys = new List<object>{ SubFloorVisuals.SubFloor };
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum SubFloorVisuals : byte
|
||||
{
|
||||
SubFloor,
|
||||
Covered, // is there a floor tile over this entity
|
||||
ScannerRevealed, // is this entity revealed by a scanner or some other entity?
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
using Content.Shared.Interaction;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.SubFloor;
|
||||
|
||||
public abstract class SharedTrayScannerSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TrayScannerComponent, ComponentGetState>(OnTrayScannerGetState);
|
||||
SubscribeLocalEvent<TrayScannerComponent, ComponentHandleState>(OnTrayScannerHandleState);
|
||||
SubscribeLocalEvent<TrayScannerComponent, ActivateInWorldEvent>(OnTrayScannerActivate);
|
||||
}
|
||||
|
||||
private void OnTrayScannerActivate(EntityUid uid, TrayScannerComponent scanner, ActivateInWorldEvent args)
|
||||
{
|
||||
ActivateTray(uid, scanner);
|
||||
}
|
||||
|
||||
private void ActivateTray(EntityUid uid, TrayScannerComponent? scanner = null)
|
||||
{
|
||||
if (!Resolve(uid, ref scanner))
|
||||
return;
|
||||
|
||||
ToggleTrayScanner(uid, !scanner.Toggled, scanner);
|
||||
if (EntityManager.TryGetComponent<AppearanceComponent>(uid, out var appearance))
|
||||
{
|
||||
appearance.SetData(TrayScannerVisual.Visual, scanner.Toggled == true ? TrayScannerVisual.On : TrayScannerVisual.Off);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ToggleTrayScanner(EntityUid uid, bool state, TrayScannerComponent? scanner = null)
|
||||
{
|
||||
if (!Resolve(uid, ref scanner))
|
||||
return;
|
||||
|
||||
scanner.Toggled = state;
|
||||
scanner.Dirty();
|
||||
}
|
||||
|
||||
private void OnTrayScannerGetState(EntityUid uid, TrayScannerComponent scanner, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new TrayScannerState(scanner.Toggled);
|
||||
}
|
||||
|
||||
private void OnTrayScannerHandleState(EntityUid uid, TrayScannerComponent scanner, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not TrayScannerState state)
|
||||
return;
|
||||
|
||||
ToggleTrayScanner(uid, state.Toggled, scanner);
|
||||
}
|
||||
|
||||
public virtual void OnSubfloorAnchored(EntityUid uid, SubFloorHideComponent? hideComp = null, TransformComponent? xform = null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum TrayScannerVisual : sbyte
|
||||
{
|
||||
Visual,
|
||||
On,
|
||||
Off
|
||||
}
|
||||
@@ -5,49 +5,26 @@ namespace Content.Shared.SubFloor
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple component that automatically hides the sibling
|
||||
/// <see cref="ISpriteComponent" /> when the tile it's on is not a sub floor
|
||||
/// <see cref="SpriteComponent" /> when the tile it's on is not a sub floor
|
||||
/// (plating).
|
||||
/// </summary>
|
||||
/// <seealso cref="P:Content.Shared.Maps.ContentTileDefinition.IsSubFloor" />
|
||||
[NetworkedComponent]
|
||||
[RegisterComponent]
|
||||
[Friend(typeof(SubFloorHideSystem))]
|
||||
[Friend(typeof(SharedSubFloorHideSystem))]
|
||||
public sealed class SubFloorHideComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the entity will be hid when not in subfloor.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("enabled")]
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the entity's current position has a "Floor-type" tile above its current position.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool IsUnderCover { get; set; } = false;
|
||||
|
||||
/*
|
||||
* An un-anchored hiding entity would require listening to on-move events in case it moves into a sub-floor
|
||||
* tile. Also T-Ray scanner headaches.
|
||||
/// <summary>
|
||||
/// This entity needs to be anchored to be hid when not in subfloor.
|
||||
/// When revealed using some scanning tool, what transparency should be used to draw this item?
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("requireAnchored")]
|
||||
public bool RequireAnchored { get; set; } = true;
|
||||
*/
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new SubFloorHideComponentState(Enabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this entity is supposed
|
||||
/// to be visible.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool Visible { get; set; }
|
||||
[DataField("scannerTransparency")]
|
||||
public float ScannerTransparency = 0.8f;
|
||||
|
||||
/// <summary>
|
||||
/// The entities this subfloor is revealed by.
|
||||
@@ -55,15 +32,4 @@ namespace Content.Shared.SubFloor
|
||||
[ViewVariables]
|
||||
public HashSet<EntityUid> RevealedBy { get; set; } = new();
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class SubFloorHideComponentState : ComponentState
|
||||
{
|
||||
public bool Enabled { get; }
|
||||
|
||||
public SubFloorHideComponentState(bool enabled)
|
||||
{
|
||||
Enabled = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.SubFloor;
|
||||
|
||||
[RegisterComponent]
|
||||
[NetworkedComponent]
|
||||
public class TrayScannerComponent : Component
|
||||
public sealed class TrayScannerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the scanner is currently on.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool Toggled { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
// this should always be rounded
|
||||
/// <summary>
|
||||
/// Last position of the scanner. Rounded to integers to avoid excessive entity lookups when moving.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public Vector2 LastLocation { get; set; }
|
||||
public Vector2i? LastLocation { get; set; }
|
||||
|
||||
// range of the scanner itself
|
||||
/// <summary>
|
||||
/// Radius in which the scanner will reveal entities. Centered on the <see cref="LastLocation"/>.
|
||||
/// </summary>
|
||||
[DataField("range")]
|
||||
public float Range { get; set; } = 2f;
|
||||
public float Range { get; set; } = 2.5f;
|
||||
|
||||
// exclude entities that are not the set
|
||||
// of entities in range & entities already revealed
|
||||
/// <summary>
|
||||
/// The sub-floor entities that this scanner is currently revealing.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public HashSet<EntityUid> RevealedSubfloors = new();
|
||||
}
|
||||
@@ -33,10 +35,10 @@ public class TrayScannerComponent : Component
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class TrayScannerState : ComponentState
|
||||
{
|
||||
public bool Toggled { get; }
|
||||
public bool Enabled;
|
||||
|
||||
public TrayScannerState(bool toggle)
|
||||
public TrayScannerState(bool enabled)
|
||||
{
|
||||
Toggled = toggle;
|
||||
Enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
247
Content.Shared/SubFloor/TrayScannerSystem.cs
Normal file
247
Content.Shared/SubFloor/TrayScannerSystem.cs
Normal file
@@ -0,0 +1,247 @@
|
||||
using Content.Shared.Interaction;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Shared.SubFloor;
|
||||
|
||||
public sealed class TrayScannerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private IMapManager _mapManager = default!;
|
||||
[Dependency] private IGameTiming _gameTiming = default!;
|
||||
[Dependency] private SharedSubFloorHideSystem _subfloorSystem = default!;
|
||||
[Dependency] private SharedContainerSystem _containerSystem = default!;
|
||||
|
||||
private HashSet<EntityUid> _activeScanners = new();
|
||||
private RemQueue<EntityUid> _invalidScanners = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TrayScannerComponent, ComponentShutdown>(OnComponentShutdown);
|
||||
SubscribeLocalEvent<TrayScannerComponent, ComponentGetState>(OnTrayScannerGetState);
|
||||
SubscribeLocalEvent<TrayScannerComponent, ComponentHandleState>(OnTrayScannerHandleState);
|
||||
SubscribeLocalEvent<TrayScannerComponent, ActivateInWorldEvent>(OnTrayScannerActivate);
|
||||
}
|
||||
|
||||
private void OnTrayScannerActivate(EntityUid uid, TrayScannerComponent scanner, ActivateInWorldEvent args)
|
||||
{
|
||||
SetScannerEnabled(uid, !scanner.Enabled, scanner);
|
||||
}
|
||||
|
||||
private void SetScannerEnabled(EntityUid uid, bool enabled, TrayScannerComponent? scanner = null)
|
||||
{
|
||||
if (!Resolve(uid, ref scanner))
|
||||
return;
|
||||
|
||||
scanner.Enabled = enabled;
|
||||
scanner.Dirty();
|
||||
|
||||
if (scanner.Enabled)
|
||||
_activeScanners.Add(uid);
|
||||
|
||||
// We don't remove from _activeScanners on disabled, because the update function will handle that, as well as
|
||||
// managing the revealed subfloor entities
|
||||
|
||||
if (EntityManager.TryGetComponent<AppearanceComponent>(uid, out var appearance))
|
||||
{
|
||||
appearance.SetData(TrayScannerVisual.Visual, scanner.Enabled == true ? TrayScannerVisual.On : TrayScannerVisual.Off);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTrayScannerGetState(EntityUid uid, TrayScannerComponent scanner, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new TrayScannerState(scanner.Enabled);
|
||||
}
|
||||
|
||||
private void OnTrayScannerHandleState(EntityUid uid, TrayScannerComponent scanner, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not TrayScannerState state)
|
||||
return;
|
||||
|
||||
SetScannerEnabled(uid, scanner.Enabled, scanner);
|
||||
|
||||
// This is hacky and somewhat inefficient for the client. But when resetting predicted entities we have to unset
|
||||
// last position. This is because appearance data gets reset, but if the position isn't reset the scanner won't
|
||||
// re-reveal entities leading to odd visuals.
|
||||
scanner.LastLocation = null;
|
||||
}
|
||||
|
||||
public void OnComponentShutdown(EntityUid uid, TrayScannerComponent scanner, ComponentShutdown args)
|
||||
{
|
||||
_subfloorSystem.SetEntitiesRevealed(scanner.RevealedSubfloors, uid, false);
|
||||
_activeScanners.Remove(uid);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
if (!_gameTiming.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
if (!_activeScanners.Any()) return;
|
||||
|
||||
foreach (var scanner in _activeScanners)
|
||||
{
|
||||
if (_invalidScanners.List != null
|
||||
&& _invalidScanners.List.Contains(scanner))
|
||||
continue;
|
||||
|
||||
if (!UpdateTrayScanner(scanner))
|
||||
_invalidScanners.Add(scanner);
|
||||
}
|
||||
|
||||
foreach (var invalidScanner in _invalidScanners)
|
||||
_activeScanners.Remove(invalidScanner);
|
||||
|
||||
_invalidScanners.List?.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When a subfloor entity gets anchored (which includes spawning & coming into PVS range), Check for nearby scanners.
|
||||
/// </summary>
|
||||
public void OnSubfloorAnchored(EntityUid uid, SubFloorHideComponent? hideComp = null, TransformComponent? xform = null)
|
||||
{
|
||||
if (!Resolve(uid, ref hideComp, ref xform))
|
||||
return;
|
||||
|
||||
var pos = xform.MapPosition;
|
||||
|
||||
foreach (var entity in _activeScanners)
|
||||
{
|
||||
if (!TryComp(entity, out TrayScannerComponent? scanner))
|
||||
continue;
|
||||
|
||||
if (!Transform(entity).MapPosition.InRange(pos, scanner.Range))
|
||||
continue;
|
||||
|
||||
hideComp.RevealedBy.Add(entity);
|
||||
scanner.RevealedSubfloors.Add(uid);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a T-Ray scanner. Should be called on immediate
|
||||
/// state change (turned on/off), or during the update
|
||||
/// loop.
|
||||
/// </summary>
|
||||
/// <returns>true if the update was successful, false otherwise</returns>
|
||||
private bool UpdateTrayScanner(EntityUid uid, TrayScannerComponent? scanner = null, TransformComponent? transform = null)
|
||||
{
|
||||
if (!Resolve(uid, ref scanner, ref transform))
|
||||
return false;
|
||||
|
||||
// if the scanner was toggled off recently,
|
||||
// set all the known subfloor to invisible,
|
||||
// and return false so it's removed from
|
||||
// the active scanner list
|
||||
if (!scanner.Enabled || transform.MapID == MapId.Nullspace)
|
||||
{
|
||||
_subfloorSystem.SetEntitiesRevealed(scanner.RevealedSubfloors, uid, false);
|
||||
scanner.LastLocation = null;
|
||||
scanner.RevealedSubfloors.Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
var pos = transform.LocalPosition;
|
||||
|
||||
// zero vector implies container
|
||||
//
|
||||
// this means we should get the entity transform's parent
|
||||
if (pos == Vector2.Zero
|
||||
&& transform.Parent != null
|
||||
&& _containerSystem.ContainsEntity(transform.ParentUid, uid))
|
||||
{
|
||||
pos = transform.Parent.LocalPosition;
|
||||
|
||||
// if this is also zero, we can check one more time
|
||||
//
|
||||
// could recurse through fully but i think that's useless,
|
||||
// just attempt to check through the gp's transform and if
|
||||
// that doesn't work, just don't bother any further
|
||||
if (pos == Vector2.Zero)
|
||||
{
|
||||
var gpTransform = transform.Parent.Parent;
|
||||
if (gpTransform != null
|
||||
&& _containerSystem.ContainsEntity(gpTransform.Owner, transform.ParentUid))
|
||||
{
|
||||
pos = gpTransform.LocalPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// is the position still logically zero? just clear,
|
||||
// but we need to keep it as 'true' since this t-ray
|
||||
// is still technically on
|
||||
if (pos == Vector2.Zero)
|
||||
{
|
||||
_subfloorSystem.SetEntitiesRevealed(scanner.RevealedSubfloors, uid, false);
|
||||
scanner.RevealedSubfloors.Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// get the rounded position so that small movements don't cause this to
|
||||
// update every time
|
||||
var flooredPos = (Vector2i) pos;
|
||||
|
||||
// MAYBE redo this. Currently different players can see different entities
|
||||
//
|
||||
// Here we avoid the entity lookup & return early if the scanner's position hasn't appreciably changed. However,
|
||||
// if a new player enters PVS-range, they will update the in-range entities on their end and use that to set
|
||||
// LastLocation. This means that different players can technically see different entities being revealed by the
|
||||
// same scanner. The correct fix for this is probably just to network the revealed entity set.... But I CBF
|
||||
// doing that right now....
|
||||
if (flooredPos == scanner.LastLocation
|
||||
|| float.IsNaN(flooredPos.X) && float.IsNaN(flooredPos.Y))
|
||||
return true;
|
||||
|
||||
scanner.LastLocation = flooredPos;
|
||||
|
||||
// Update entities in Range
|
||||
HashSet<EntityUid> nearby = new();
|
||||
var coords = transform.MapPosition;
|
||||
var worldBox = Box2.CenteredAround(coords.Position, (scanner.Range * 2, scanner.Range * 2));
|
||||
|
||||
// For now, limiting to the scanner's own grid. We could do a grid-lookup, but then what do we do if one grid
|
||||
// flies away, while the scanner's local-position remains unchanged?
|
||||
if (_mapManager.TryGetGrid(transform.GridID, out var grid))
|
||||
{
|
||||
foreach (var entity in grid.GetAnchoredEntities(worldBox))
|
||||
{
|
||||
if (!Transform(entity).MapPosition.InRange(coords, scanner.Range))
|
||||
continue;
|
||||
|
||||
if (!TryComp(entity, out SubFloorHideComponent? hideComp))
|
||||
continue; // Not a hide-able entity.
|
||||
|
||||
nearby.Add(entity);
|
||||
|
||||
if (scanner.RevealedSubfloors.Add(entity))
|
||||
_subfloorSystem.SetEntityRevealed(entity, uid, true, hideComp);
|
||||
}
|
||||
}
|
||||
|
||||
// get all the old elements that are no longer detected
|
||||
HashSet<EntityUid> missing = new(scanner.RevealedSubfloors.Except(nearby));
|
||||
|
||||
// remove those from the list
|
||||
scanner.RevealedSubfloors.ExceptWith(missing);
|
||||
|
||||
// and hide them
|
||||
_subfloorSystem.SetEntitiesRevealed(missing, uid, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum TrayScannerVisual : sbyte
|
||||
{
|
||||
Visual,
|
||||
On,
|
||||
Off
|
||||
}
|
||||
Reference in New Issue
Block a user