using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Utility;
namespace Robust.Shared.ComponentTrees;
///
/// This system will recursively raise events to update component tree positions any time any entity moves.
///
///
/// This is used by some client-side systems (e.g., sprites, lights, etc). However this can be quite expensive and if possible should not be used by the server.
///
internal sealed class RecursiveMoveSystem : EntitySystem
{
[Dependency] private readonly SharedTransformSystem _transform = default!;
public delegate void TreeRecursiveMoveEventHandler(EntityUid uid, TransformComponent xform);
public event TreeRecursiveMoveEventHandler? OnTreeRecursiveMove;
private EntityQuery _mapQuery;
private EntityQuery _gridQuery;
private EntityQuery _xformQuery;
private bool _subscribed = false;
public override void Initialize()
{
base.Initialize();
_gridQuery = GetEntityQuery();
_mapQuery = GetEntityQuery();
_xformQuery = GetEntityQuery();
}
public override void Shutdown()
{
if (_subscribed)
_transform.OnBeforeMoveEvent -= AnythingMoved;
_subscribed = false;
}
internal void AddSubscription()
{
if (_subscribed)
return;
_subscribed = true;
_transform.OnBeforeMoveEvent += AnythingMoved;
}
private void AnythingMoved(ref MoveEvent args)
{
if (args.Component.MapUid == args.Sender || args.Component.GridUid == args.Sender)
return;
DebugTools.Assert(!_mapQuery.HasComp(args.Sender));
DebugTools.Assert(!_gridQuery.HasComp(args.Sender));
AnythingMovedSubHandler(args.Sender, args.Component);
}
private void AnythingMovedSubHandler(
EntityUid uid,
TransformComponent xform)
{
OnTreeRecursiveMove?.Invoke(uid, xform);
// TODO only enumerate over entities in containers if necessary?
// annoyingly, containers aren't guaranteed to occlude sprites & lights
// but AFAIK thats currently unused???
foreach (var child in xform._children)
{
if (_xformQuery.TryGetComponent(child, out var childXform))
AnythingMovedSubHandler(child, childXform);
}
}
}