Files
tbd-station-14/Content.Client/SubFloor/TrayScannerSystem.cs
Nemanja 524725d378 HandsSystem Refactor (#38438)
* checkpoint

* pt 2

* pt... i forgot

* pt 4

* patch

* More test fixes

* optimization!!!

* the REAL hand system

* fix RetractableItemActionSystem.cs oversight

* the review

* test

* remove test usage of body prototype

* Update Content.IntegrationTests/Tests/Interaction/InteractionTest.cs

Co-authored-by: Tayrtahn <tayrtahn@gmail.com>

* hellcode

* hellcode 2

* Minor cleanup

* test

* Chasing the last of the bugs

* changes

---------

Co-authored-by: Tayrtahn <tayrtahn@gmail.com>
2025-06-25 15:13:03 +02:00

178 lines
6.9 KiB
C#

using Content.Shared.Hands.EntitySystems;
using Content.Shared.Inventory;
using Content.Shared.SubFloor;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Shared.Timing;
namespace Content.Client.SubFloor;
public sealed class TrayScannerSystem : SharedTrayScannerSystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly AnimationPlayerSystem _animation = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly TrayScanRevealSystem _trayScanReveal = default!;
private const string TRayAnimationKey = "trays";
private const double AnimationLength = 0.3;
public const LookupFlags Flags = LookupFlags.Static | LookupFlags.Sundries | LookupFlags.Approximate;
public override void Update(float frameTime)
{
base.Update(frameTime);
if (!_timing.IsFirstTimePredicted)
return;
// TODO: Multiple viewports or w/e
var player = _player.LocalEntity;
var xformQuery = GetEntityQuery<TransformComponent>();
if (!xformQuery.TryGetComponent(player, out var playerXform))
return;
var playerPos = _transform.GetWorldPosition(playerXform, xformQuery);
var playerMap = playerXform.MapID;
var range = 0f;
HashSet<Entity<SubFloorHideComponent>> inRange;
var scannerQuery = GetEntityQuery<TrayScannerComponent>();
// TODO: Should probably sub to player attached changes / inventory changes but inventory's
// API is extremely skrungly. If this ever shows up on dottrace ping me and laugh.
var canSee = false;
// TODO: Common iterator for both systems.
if (_inventory.TryGetContainerSlotEnumerator(player.Value, out var enumerator))
{
while (enumerator.MoveNext(out var slot))
{
foreach (var ent in slot.ContainedEntities)
{
if (!scannerQuery.TryGetComponent(ent, out var sneakScanner) || !sneakScanner.Enabled)
continue;
canSee = true;
range = MathF.Max(range, sneakScanner.Range);
}
}
}
foreach (var hand in _hands.EnumerateHands(player.Value))
{
if (!_hands.TryGetHeldItem(player.Value, hand, out var heldEntity))
continue;
if (!scannerQuery.TryGetComponent(heldEntity, out var heldScanner) || !heldScanner.Enabled)
continue;
range = MathF.Max(heldScanner.Range, range);
canSee = true;
break;
}
inRange = new HashSet<Entity<SubFloorHideComponent>>();
if (canSee)
{
_lookup.GetEntitiesInRange(playerMap, playerPos, range, inRange, flags: Flags);
foreach (var (uid, comp) in inRange)
{
if (comp.IsUnderCover || _trayScanReveal.IsUnderRevealingEntity(uid))
EnsureComp<TrayRevealedComponent>(uid);
}
}
var revealedQuery = AllEntityQuery<TrayRevealedComponent, SpriteComponent>();
var subfloorQuery = GetEntityQuery<SubFloorHideComponent>();
while (revealedQuery.MoveNext(out var uid, out _, out var sprite))
{
// Revealing
// Add buffer range to avoid flickers.
if (subfloorQuery.TryGetComponent(uid, out var subfloor) &&
inRange.Contains((uid, subfloor)))
{
// Due to the fact client is predicting this server states will reset it constantly
if ((!_appearance.TryGetData(uid, SubFloorVisuals.ScannerRevealed, out bool value) || !value) &&
sprite.Color.A > SubfloorRevealAlpha)
{
_sprite.SetColor((uid, sprite), sprite.Color.WithAlpha(0f));
}
SetRevealed(uid, true);
if (sprite.Color.A >= SubfloorRevealAlpha || _animation.HasRunningAnimation(uid, TRayAnimationKey))
continue;
_animation.Play(uid, new Animation()
{
Length = TimeSpan.FromSeconds(AnimationLength),
AnimationTracks =
{
new AnimationTrackComponentProperty()
{
ComponentType = typeof(SpriteComponent),
Property = nameof(SpriteComponent.Color),
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(sprite.Color.WithAlpha(0f), 0f),
new AnimationTrackProperty.KeyFrame(sprite.Color.WithAlpha(SubfloorRevealAlpha), (float) AnimationLength)
}
}
}
}, TRayAnimationKey);
}
// Hiding
else
{
// Hidden completely so unreveal and reset the alpha.
if (sprite.Color.A <= 0f)
{
SetRevealed(uid, false);
RemCompDeferred<TrayRevealedComponent>(uid);
_sprite.SetColor((uid, sprite), sprite.Color.WithAlpha(1f));
continue;
}
SetRevealed(uid, true);
if (_animation.HasRunningAnimation(uid, TRayAnimationKey))
continue;
_animation.Play(uid, new Animation()
{
Length = TimeSpan.FromSeconds(AnimationLength),
AnimationTracks =
{
new AnimationTrackComponentProperty()
{
ComponentType = typeof(SpriteComponent),
Property = nameof(SpriteComponent.Color),
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(sprite.Color, 0f),
new AnimationTrackProperty.KeyFrame(sprite.Color.WithAlpha(0f), (float) AnimationLength)
}
}
}
}, TRayAnimationKey);
}
}
}
private void SetRevealed(EntityUid uid, bool value)
{
_appearance.SetData(uid, SubFloorVisuals.ScannerRevealed, value);
}
}