--- /dev/null
+using Content.Server.Implants;
+using Robust.Shared.Audio;
+
+namespace Content.Server.Implants.Components;
+
+/// <summary>
+/// Randomly teleports entity when triggered.
+/// </summary>
+[RegisterComponent]
+public sealed partial class ScramImplantComponent : Component
+{
+ /// <summary>
+ /// Up to how far to teleport the user
+ /// </summary>
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public float TeleportRadius = 100f;
+
+ /// <summary>
+ /// How many times to check for a valid tile to teleport to
+ /// </summary>
+ [DataField, ViewVariables(VVAccess.ReadOnly)]
+ public int TeleportAttempts = 20;
+
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public SoundSpecifier TeleportSound = new SoundPathSpecifier("/Audio/Effects/teleport_arrival.ogg");
+}
using Content.Server.Cuffs;
using Content.Server.Forensics;
using Content.Server.Humanoid;
+using Content.Server.Implants.Components;
using Content.Server.Store.Components;
using Content.Server.Store.Systems;
using Content.Shared.Cuffs.Components;
using Content.Shared.Implants;
using Content.Shared.Implants.Components;
using Content.Shared.Interaction;
+using Content.Shared.Physics;
using Content.Shared.Popups;
using Content.Shared.Preferences;
+using Robust.Shared.Audio.Systems;
+using Robust.Shared.Map;
+using Robust.Shared.Maths;
+using Robust.Shared.Physics;
+using Robust.Shared.Physics.Components;
+using Robust.Shared.Random;
+using System.Numerics;
namespace Content.Server.Implants;
{
[Dependency] private readonly CuffableSystem _cuffable = default!;
[Dependency] private readonly HumanoidAppearanceSystem _humanoidAppearance = default!;
+ [Dependency] private readonly IMapManager _mapManager = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly StoreSystem _store = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
+ [Dependency] private readonly SharedTransformSystem _xform = default!;
[Dependency] private readonly ForensicsSystem _forensicsSystem = default!;
+ private EntityQuery<PhysicsComponent> _physicsQuery;
+
public override void Initialize()
{
base.Initialize();
+ _physicsQuery = GetEntityQuery<PhysicsComponent>();
+
SubscribeLocalEvent<SubdermalImplantComponent, UseFreedomImplantEvent>(OnFreedomImplant);
SubscribeLocalEvent<StoreComponent, ImplantRelayEvent<AfterInteractUsingEvent>>(OnStoreRelay);
SubscribeLocalEvent<SubdermalImplantComponent, ActivateImplantEvent>(OnActivateImplantEvent);
+ SubscribeLocalEvent<SubdermalImplantComponent, UseScramImplantEvent>(OnScramImplant);
SubscribeLocalEvent<SubdermalImplantComponent, UseDnaScramblerImplantEvent>(OnDnaScramblerImplant);
}
args.Handled = true;
}
+ private void OnScramImplant(EntityUid uid, SubdermalImplantComponent component, UseScramImplantEvent args)
+ {
+ if (component.ImplantedEntity is not { } ent)
+ return;
+
+ if (!TryComp<ScramImplantComponent>(uid, out var implant))
+ return;
+
+ var xform = Transform(ent);
+ var entityCoords = xform.Coordinates.ToMap(EntityManager);
+
+ // try to find a valid position to teleport to, teleport to whatever works if we can't
+ var targetCoords = new MapCoordinates();
+ for (var i = 0; i < implant.TeleportAttempts; i++)
+ {
+ var distance = implant.TeleportRadius * MathF.Sqrt(_random.NextFloat()); // to get an uniform distribution
+ targetCoords = entityCoords.Offset(_random.NextAngle().ToVec() * distance);
+
+ // prefer teleporting to grids
+ if (!_mapManager.TryFindGridAt(targetCoords, out var gridUid, out var grid))
+ continue;
+
+ // the implant user probably does not want to be in your walls
+ var valid = true;
+ foreach (var entity in grid.GetAnchoredEntities(targetCoords))
+ {
+ if (!_physicsQuery.TryGetComponent(entity, out var body))
+ continue;
+
+ if (body.BodyType != BodyType.Static ||
+ !body.Hard ||
+ (body.CollisionLayer & (int) CollisionGroup.Impassable) == 0)
+ continue;
+
+ valid = false;
+ break;
+ }
+ if (valid)
+ break;
+ }
+ _xform.SetWorldPosition(ent, targetCoords.Position);
+ _audio.PlayPvs(implant.TeleportSound, ent);
+
+ args.Handled = true;
+ }
+
private void OnDnaScramblerImplant(EntityUid uid, SubdermalImplantComponent component, UseDnaScramblerImplantEvent args)
{
if (component.ImplantedEntity is not { } ent)