]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Replace the teleportation logic on the SCRAM implant! (#26429)
authornikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com>
Mon, 1 Apr 2024 06:31:36 +0000 (06:31 +0000)
committerGitHub <noreply@github.com>
Mon, 1 Apr 2024 06:31:36 +0000 (17:31 +1100)
* Replace the teleportation logic on the SCRAM implant!

Now instead of just trying to pick a random tile in range 20 times, the
scram teleportation logic now:

- Gets a list of grids in range
- Until a suitable tile is picked it picks a random grid
- From that grid it picks a random tile.
- If the tile is suitable, then it is set as the target and the user
  will be teleported there.
- Grids and tiles are randomly picked as outlined above until a valid
  tile is found, or all valid grids and tiles are exhausted.
- Should no suitable tile be found then they get teleported to the same
  position they are at. Effectively not teleporting them.

* Actually make the defaults sane which I forgor in the last commit

* Extract tile section to its own function. Bias selection for current grid. Use proper coords for box.

* Address reviews as much as possible

* Address reviews

Content.Server/Implants/Components/ScramImplantComponent.cs
Content.Server/Implants/SubdermalImplantSystem.cs

index 88c433abfbe9b8eeb66092be939fd8dd0b7e6e73..f3bbc9e584274e3266aa0c1e7dde60d8ec73f598 100644 (file)
@@ -15,12 +15,6 @@ public sealed partial class ScramImplantComponent : Component
     [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");
 }
index fd95720732b121c04ab92d6c4996c2b2b8d41ed3..e8af08b2ebbb9f98b289e230ece133190d17d93b 100644 (file)
@@ -14,13 +14,14 @@ 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;
 using Content.Shared.Movement.Pulling.Components;
 using Content.Shared.Movement.Pulling.Systems;
+using Robust.Shared.Collections;
+using Robust.Shared.Map.Components;
 
 namespace Content.Server.Implants;
 
@@ -28,7 +29,6 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem
 {
     [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!;
@@ -37,8 +37,11 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem
     [Dependency] private readonly SharedTransformSystem _xform = default!;
     [Dependency] private readonly ForensicsSystem _forensicsSystem = default!;
     [Dependency] private readonly PullingSystem _pullingSystem = default!;
+    [Dependency] private readonly EntityLookupSystem _lookupSystem = default!;
+    [Dependency] private readonly SharedMapSystem _mapSystem = default!;
 
     private EntityQuery<PhysicsComponent> _physicsQuery;
+    private HashSet<Entity<MapGridComponent>> _targetGrids = [];
 
     public override void Initialize()
     {
@@ -107,41 +110,92 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem
             _pullingSystem.TryStopPull(ent, pull);
 
         var xform = Transform(ent);
-        var entityCoords = xform.Coordinates.ToMap(EntityManager, _xform);
+        var targetCoords = SelectRandomTileInRange(xform, implant.TeleportRadius);
 
-        // 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++)
+        if (targetCoords != null)
         {
-            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;
+            _xform.SetCoordinates(ent, targetCoords.Value);
+            _audio.PlayPvs(implant.TeleportSound, ent);
+            args.Handled = true;
+        }
+    }
 
-            // the implant user probably does not want to be in your walls
-            var valid = true;
-            foreach (var entity in grid.GetAnchoredEntities(targetCoords))
+    private EntityCoordinates? SelectRandomTileInRange(TransformComponent userXform, float radius)
+    {
+        var userCoords = userXform.Coordinates.ToMap(EntityManager, _xform);
+        _targetGrids.Clear();
+        _lookupSystem.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))
             {
-                if (!_physicsQuery.TryGetComponent(entity, out var body))
-                    continue;
+                _targetGrids.Remove(userGrid);
+                targetGrid = userGrid;
+            }
+        }
 
-                if (body.BodyType != BodyType.Static ||
-                    !body.Hard ||
-                    (body.CollisionLayer & (int) CollisionGroup.Impassable) == 0)
-                    continue;
+        if (targetGrid == null)
+            targetGrid = _random.GetRandom().PickAndTake(_targetGrids);
 
-                valid = false;
-                break;
+        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 = _mapSystem.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 _mapSystem.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,
+                        _mapSystem.TileCenterToVector(targetGrid.Value, tile));
+                    break;
+                }
             }
-            if (valid)
+
+            if (valid || _targetGrids.Count == 0) // if we don't do the check here then PickAndTake will blow up on an empty set.
                 break;
-        }
-        _xform.SetWorldPosition(ent, targetCoords.Position);
-        _audio.PlayPvs(implant.TeleportSound, ent);
 
-        args.Handled = true;
+            targetGrid = _random.GetRandom().PickAndTake(_targetGrids);
+        } while (true);
+
+        return targetCoords;
     }
 
     private void OnDnaScramblerImplant(EntityUid uid, SubdermalImplantComponent component, UseDnaScramblerImplantEvent args)