* Push 1 * cleanup + master merge * launchontrigger * A crumb of cleanup --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
142 lines
5.4 KiB
C#
142 lines
5.4 KiB
C#
using System.Numerics;
|
|
using Content.Shared.Movement.Pulling.Components;
|
|
using Content.Shared.Movement.Pulling.Systems;
|
|
using Content.Shared.Physics;
|
|
using Content.Shared.Trigger.Components.Effects;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Map.Components;
|
|
using Robust.Shared.Network;
|
|
using Robust.Shared.Physics;
|
|
using Robust.Shared.Physics.Components;
|
|
using Robust.Shared.Audio.Systems;
|
|
using Robust.Shared.Collections;
|
|
using Robust.Shared.Random;
|
|
|
|
namespace Content.Shared.Trigger.Systems;
|
|
|
|
public sealed class ScramOnTriggerSystem : XOnTriggerSystem<ScramOnTriggerComponent>
|
|
{
|
|
[Dependency] private readonly PullingSystem _pulling = default!;
|
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
|
[Dependency] private readonly IRobustRandom _random = default!;
|
|
[Dependency] private readonly SharedMapSystem _map = default!;
|
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
|
[Dependency] private readonly INetManager _net = default!;
|
|
|
|
private EntityQuery<PhysicsComponent> _physicsQuery;
|
|
private HashSet<Entity<MapGridComponent>> _targetGrids = new();
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
|
}
|
|
|
|
protected override void OnTrigger(Entity<ScramOnTriggerComponent> ent, EntityUid target, ref TriggerEvent args)
|
|
{
|
|
// We need stop the user from being pulled so they don't just get "attached" with whoever is pulling them.
|
|
// This can for example happen when the user is cuffed and being pulled.
|
|
if (TryComp<PullableComponent>(target, out var pull) && _pulling.IsPulled(target, pull))
|
|
_pulling.TryStopPull(ent, pull);
|
|
|
|
// Check if the user is pulling anything, and drop it if so.
|
|
if (TryComp<PullerComponent>(target, out var puller) && TryComp<PullableComponent>(puller.Pulling, out var pullable))
|
|
_pulling.TryStopPull(puller.Pulling.Value, pullable);
|
|
|
|
_audio.PlayPredicted(ent.Comp.TeleportSound, ent, args.User);
|
|
|
|
// Can't predict picking random grids and the target location might be out of PVS range.
|
|
if (_net.IsClient)
|
|
return;
|
|
|
|
var xform = Transform(target);
|
|
var targetCoords = SelectRandomTileInRange(xform, ent.Comp.TeleportRadius);
|
|
|
|
if (targetCoords != null)
|
|
{
|
|
_transform.SetCoordinates(target, targetCoords.Value);
|
|
args.Handled = true;
|
|
}
|
|
}
|
|
|
|
private EntityCoordinates? SelectRandomTileInRange(TransformComponent userXform, float radius)
|
|
{
|
|
var userCoords = _transform.ToMapCoordinates(userXform.Coordinates);
|
|
_targetGrids.Clear();
|
|
_lookup.GetEntitiesInRange(userCoords, radius, _targetGrids);
|
|
Entity<MapGridComponent>? targetGrid = null;
|
|
|
|
if (_targetGrids.Count == 0)
|
|
return null;
|
|
|
|
// Give preference to the grid the entity is currently on.
|
|
// This does not guarantee that if the probability fails that the owner's grid won't be picked.
|
|
// In reality the probability is higher and depends on the number of grids.
|
|
if (userXform.GridUid != null && TryComp<MapGridComponent>(userXform.GridUid, out var gridComp))
|
|
{
|
|
var userGrid = new Entity<MapGridComponent>(userXform.GridUid.Value, gridComp);
|
|
if (_random.Prob(0.5f))
|
|
{
|
|
_targetGrids.Remove(userGrid);
|
|
targetGrid = userGrid;
|
|
}
|
|
}
|
|
|
|
if (targetGrid == null)
|
|
targetGrid = _random.GetRandom().PickAndTake(_targetGrids);
|
|
|
|
EntityCoordinates? targetCoords = null;
|
|
|
|
do
|
|
{
|
|
var valid = false;
|
|
|
|
var range = (float)Math.Sqrt(radius);
|
|
var box = Box2.CenteredAround(userCoords.Position, new Vector2(range, range));
|
|
var tilesInRange = _map.GetTilesEnumerator(targetGrid.Value.Owner, targetGrid.Value.Comp, box, false);
|
|
var tileList = new ValueList<Vector2i>();
|
|
|
|
while (tilesInRange.MoveNext(out var tile))
|
|
{
|
|
tileList.Add(tile.GridIndices);
|
|
}
|
|
|
|
while (tileList.Count != 0)
|
|
{
|
|
var tile = tileList.RemoveSwap(_random.Next(tileList.Count));
|
|
valid = true;
|
|
foreach (var entity in _map.GetAnchoredEntities(targetGrid.Value.Owner, targetGrid.Value.Comp,
|
|
tile))
|
|
{
|
|
if (!_physicsQuery.TryGetComponent(entity, out var body))
|
|
continue;
|
|
|
|
if (body.BodyType != BodyType.Static ||
|
|
!body.Hard ||
|
|
(body.CollisionLayer & (int)CollisionGroup.MobMask) == 0)
|
|
continue;
|
|
|
|
valid = false;
|
|
break;
|
|
}
|
|
|
|
if (valid)
|
|
{
|
|
targetCoords = new EntityCoordinates(targetGrid.Value.Owner,
|
|
_map.TileCenterToVector(targetGrid.Value, tile));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (valid || _targetGrids.Count == 0) // if we don't do the check here then PickAndTake will blow up on an empty set.
|
|
break;
|
|
|
|
targetGrid = _random.GetRandom().PickAndTake(_targetGrids);
|
|
} while (true);
|
|
|
|
return targetCoords;
|
|
}
|
|
}
|