From d722f837a57275f2a04b3d6f16bf9c506d3af5ba Mon Sep 17 00:00:00 2001 From: nikthechampiongr Date: Mon, 25 Mar 2024 17:45:05 +0200 Subject: [PATCH 1/5] 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. --- .../Components/ScramImplantComponent.cs | 6 -- .../Implants/SubdermalImplantSystem.cs | 63 +++++++++++++------ 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/Content.Server/Implants/Components/ScramImplantComponent.cs b/Content.Server/Implants/Components/ScramImplantComponent.cs index 88c433abfbe9b8..f3bbc9e584274e 100644 --- a/Content.Server/Implants/Components/ScramImplantComponent.cs +++ b/Content.Server/Implants/Components/ScramImplantComponent.cs @@ -15,12 +15,6 @@ public sealed partial class ScramImplantComponent : Component [DataField, ViewVariables(VVAccess.ReadWrite)] public float TeleportRadius = 100f; - /// - /// How many times to check for a valid tile to teleport to - /// - [DataField, ViewVariables(VVAccess.ReadOnly)] - public int TeleportAttempts = 20; - [DataField, ViewVariables(VVAccess.ReadWrite)] public SoundSpecifier TeleportSound = new SoundPathSpecifier("/Audio/Effects/teleport_arrival.ogg"); } diff --git a/Content.Server/Implants/SubdermalImplantSystem.cs b/Content.Server/Implants/SubdermalImplantSystem.cs index 6b58f6eb0920dd..9c2f180692d5af 100644 --- a/Content.Server/Implants/SubdermalImplantSystem.cs +++ b/Content.Server/Implants/SubdermalImplantSystem.cs @@ -1,4 +1,5 @@ -using Content.Server.Cuffs; +using System.Linq; +using Content.Server.Cuffs; using Content.Server.Forensics; using Content.Server.Humanoid; using Content.Server.Implants.Components; @@ -21,6 +22,7 @@ using System.Numerics; using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Pulling.Systems; +using Robust.Shared.Map.Components; namespace Content.Server.Implants; @@ -37,6 +39,8 @@ 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 _physicsQuery; @@ -109,35 +113,54 @@ private void OnScramImplant(EntityUid uid, SubdermalImplantComponent component, var xform = Transform(ent); var entityCoords = xform.Coordinates.ToMap(EntityManager, _xform); - // 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 grids = _lookupSystem.GetEntitiesInRange(entityCoords, implant.TeleportRadius).ToList(); + _random.Shuffle(grids); + var targetCoords = entityCoords; // If we somehow fail to find a suitable tile then we essentially we just don't teleport. + + foreach (var grid in grids) { - var distance = implant.TeleportRadius * MathF.Sqrt(_random.NextFloat()); // to get an uniform distribution - targetCoords = entityCoords.Offset(_random.NextAngle().ToVec() * distance); + var valid = false; - // prefer teleporting to grids - if (!_mapManager.TryFindGridAt(targetCoords, out var gridUid, out var grid)) - continue; + var range = (float) Math.Sqrt(implant.TeleportRadius); + var box = Box2.CenteredAround(entityCoords.Position, new Vector2(range, range)); + var tilesInRange = _mapSystem.GetTilesEnumerator(grid.Owner, grid.Comp, box, false); + var tileList = new List(); - // the implant user probably does not want to be in your walls - var valid = true; - foreach (var entity in grid.GetAnchoredEntities(targetCoords)) + while (tilesInRange.MoveNext(out var tile)) { - if (!_physicsQuery.TryGetComponent(entity, out var body)) - continue; + tileList.Add(tile); + } - if (body.BodyType != BodyType.Static || - !body.Hard || - (body.CollisionLayer & (int) CollisionGroup.Impassable) == 0) - continue; + _random.Shuffle(tileList); - valid = false; - break; + foreach (var tile in tileList) + { + valid = true; + foreach(var entity in _mapSystem.GetAnchoredEntities(grid.Owner, grid.Comp, tile.GridIndices)) + { + 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) + { + targetCoords = _mapSystem.GridTileToWorld(grid.Owner, grid.Comp, tile.GridIndices); + break; + } } + if (valid) break; } + _xform.SetWorldPosition(ent, targetCoords.Position); _xform.AttachToGridOrMap(ent, xform); _audio.PlayPvs(implant.TeleportSound, ent); From 7afce78ff539634959eccd13043b682cd4a71ada Mon Sep 17 00:00:00 2001 From: nikthechampiongr Date: Mon, 25 Mar 2024 17:58:21 +0200 Subject: [PATCH 2/5] Actually make the defaults sane which I forgor in the last commit --- Content.Server/Implants/SubdermalImplantSystem.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Content.Server/Implants/SubdermalImplantSystem.cs b/Content.Server/Implants/SubdermalImplantSystem.cs index 9c2f180692d5af..db94edccb456cf 100644 --- a/Content.Server/Implants/SubdermalImplantSystem.cs +++ b/Content.Server/Implants/SubdermalImplantSystem.cs @@ -115,7 +115,7 @@ private void OnScramImplant(EntityUid uid, SubdermalImplantComponent component, var grids = _lookupSystem.GetEntitiesInRange(entityCoords, implant.TeleportRadius).ToList(); _random.Shuffle(grids); - var targetCoords = entityCoords; // If we somehow fail to find a suitable tile then we essentially we just don't teleport. + MapCoordinates? targetCoords = null; foreach (var grid in grids) { @@ -161,11 +161,13 @@ private void OnScramImplant(EntityUid uid, SubdermalImplantComponent component, break; } - _xform.SetWorldPosition(ent, targetCoords.Position); - _xform.AttachToGridOrMap(ent, xform); - _audio.PlayPvs(implant.TeleportSound, ent); - - args.Handled = true; + if (targetCoords != null) + { + _xform.SetWorldPosition(ent, targetCoords.Value.Position); + _xform.AttachToGridOrMap(ent, xform); + _audio.PlayPvs(implant.TeleportSound, ent); + args.Handled = true; + } } private void OnDnaScramblerImplant(EntityUid uid, SubdermalImplantComponent component, UseDnaScramblerImplantEvent args) From ec42aea86cb0d9f87663b4e2008b0dc9924e07f9 Mon Sep 17 00:00:00 2001 From: nikthechampiongr Date: Tue, 26 Mar 2024 18:43:11 +0200 Subject: [PATCH 3/5] Extract tile section to its own function. Bias selection for current grid. Use proper coords for box. --- .../Implants/SubdermalImplantSystem.cs | 43 ++++++++++++++----- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/Content.Server/Implants/SubdermalImplantSystem.cs b/Content.Server/Implants/SubdermalImplantSystem.cs index db94edccb456cf..61cd0b0a90870e 100644 --- a/Content.Server/Implants/SubdermalImplantSystem.cs +++ b/Content.Server/Implants/SubdermalImplantSystem.cs @@ -111,18 +111,45 @@ private void OnScramImplant(EntityUid uid, SubdermalImplantComponent component, _pullingSystem.TryStopPull(ent, pull); var xform = Transform(ent); - var entityCoords = xform.Coordinates.ToMap(EntityManager, _xform); + var targetCoords = SelectRandomTileInRange(xform, implant.TeleportRadius); - var grids = _lookupSystem.GetEntitiesInRange(entityCoords, implant.TeleportRadius).ToList(); + if (targetCoords != null) + { + _xform.SetWorldPosition(ent, targetCoords.Value.Position); + _xform.AttachToGridOrMap(ent, xform); + _audio.PlayPvs(implant.TeleportSound, ent); + args.Handled = true; + } + } + + private MapCoordinates? SelectRandomTileInRange(TransformComponent userXform, float radius) + { + var userCoords = userXform.Coordinates.ToMap(EntityManager, _xform); + var grids = _lookupSystem.GetEntitiesInRange(userCoords, radius).ToList(); _random.Shuffle(grids); + + // Give preference to the grid the entity is currently on. + var idx = grids.FindIndex(grid => grid.Owner == userXform.GridUid); + if (idx != -1) + { + if (_random.Prob(0.66f)) + { + (grids[0], grids[idx]) = (grids[idx], grids[0]); + } + else + { + (grids[^1], grids[idx]) = (grids[idx], grids[^1]); + } + } + MapCoordinates? targetCoords = null; foreach (var grid in grids) { var valid = false; - var range = (float) Math.Sqrt(implant.TeleportRadius); - var box = Box2.CenteredAround(entityCoords.Position, new Vector2(range, range)); + var range = (float) Math.Sqrt(radius); + var box = Box2.CenteredAround(userCoords.Position, new Vector2(range, range)); var tilesInRange = _mapSystem.GetTilesEnumerator(grid.Owner, grid.Comp, box, false); var tileList = new List(); @@ -161,13 +188,7 @@ private void OnScramImplant(EntityUid uid, SubdermalImplantComponent component, break; } - if (targetCoords != null) - { - _xform.SetWorldPosition(ent, targetCoords.Value.Position); - _xform.AttachToGridOrMap(ent, xform); - _audio.PlayPvs(implant.TeleportSound, ent); - args.Handled = true; - } + return targetCoords; } private void OnDnaScramblerImplant(EntityUid uid, SubdermalImplantComponent component, UseDnaScramblerImplantEvent args) From c77aa134b37c9a5ad67982006c69b9d82c0cef7b Mon Sep 17 00:00:00 2001 From: nikthechampiongr Date: Thu, 28 Mar 2024 16:48:41 +0200 Subject: [PATCH 4/5] Address reviews as much as possible --- .../Implants/SubdermalImplantSystem.cs | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/Content.Server/Implants/SubdermalImplantSystem.cs b/Content.Server/Implants/SubdermalImplantSystem.cs index 61cd0b0a90870e..5ef556bd1f0a35 100644 --- a/Content.Server/Implants/SubdermalImplantSystem.cs +++ b/Content.Server/Implants/SubdermalImplantSystem.cs @@ -15,13 +15,13 @@ 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; @@ -30,7 +30,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!; @@ -115,14 +114,14 @@ private void OnScramImplant(EntityUid uid, SubdermalImplantComponent component, if (targetCoords != null) { - _xform.SetWorldPosition(ent, targetCoords.Value.Position); + _xform.SetCoordinates(ent, targetCoords.Value); _xform.AttachToGridOrMap(ent, xform); _audio.PlayPvs(implant.TeleportSound, ent); args.Handled = true; } } - private MapCoordinates? SelectRandomTileInRange(TransformComponent userXform, float radius) + private EntityCoordinates? SelectRandomTileInRange(TransformComponent userXform, float radius) { var userCoords = userXform.Coordinates.ToMap(EntityManager, _xform); var grids = _lookupSystem.GetEntitiesInRange(userCoords, radius).ToList(); @@ -142,7 +141,7 @@ private void OnScramImplant(EntityUid uid, SubdermalImplantComponent component, } } - MapCoordinates? targetCoords = null; + EntityCoordinates? targetCoords = null; foreach (var grid in grids) { @@ -151,26 +150,25 @@ private void OnScramImplant(EntityUid uid, SubdermalImplantComponent component, var range = (float) Math.Sqrt(radius); var box = Box2.CenteredAround(userCoords.Position, new Vector2(range, range)); var tilesInRange = _mapSystem.GetTilesEnumerator(grid.Owner, grid.Comp, box, false); - var tileList = new List(); + var tileList = new ValueList(); while (tilesInRange.MoveNext(out var tile)) { - tileList.Add(tile); + tileList.Add(tile.GridIndices); } - _random.Shuffle(tileList); - - foreach (var tile in tileList) + while (tileList.Count != 0) { + var tile = tileList.RemoveSwap(_random.Next(tileList.Count)); valid = true; - foreach(var entity in _mapSystem.GetAnchoredEntities(grid.Owner, grid.Comp, tile.GridIndices)) + foreach (var entity in _mapSystem.GetAnchoredEntities(grid.Owner, grid.Comp, tile)) { if (!_physicsQuery.TryGetComponent(entity, out var body)) continue; if (body.BodyType != BodyType.Static || !body.Hard || - (body.CollisionLayer & (int) CollisionGroup.Impassable) == 0) + (body.CollisionLayer & (int) CollisionGroup.MobMask) == 0) continue; valid = false; @@ -179,7 +177,7 @@ private void OnScramImplant(EntityUid uid, SubdermalImplantComponent component, if (valid) { - targetCoords = _mapSystem.GridTileToWorld(grid.Owner, grid.Comp, tile.GridIndices); + targetCoords = new EntityCoordinates(grid.Owner, _mapSystem.TileCenterToVector(grid, tile)); break; } } From afd39f495dbdee7b42e0540fb1e87aca52078b6e Mon Sep 17 00:00:00 2001 From: nikthechampiongr Date: Sat, 30 Mar 2024 11:56:15 +0200 Subject: [PATCH 5/5] Address reviews --- .../Implants/SubdermalImplantSystem.cs | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/Content.Server/Implants/SubdermalImplantSystem.cs b/Content.Server/Implants/SubdermalImplantSystem.cs index 5ef556bd1f0a35..2ee441a54ca9a7 100644 --- a/Content.Server/Implants/SubdermalImplantSystem.cs +++ b/Content.Server/Implants/SubdermalImplantSystem.cs @@ -1,5 +1,4 @@ -using System.Linq; -using Content.Server.Cuffs; +using Content.Server.Cuffs; using Content.Server.Forensics; using Content.Server.Humanoid; using Content.Server.Implants.Components; @@ -42,6 +41,7 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem [Dependency] private readonly SharedMapSystem _mapSystem = default!; private EntityQuery _physicsQuery; + private HashSet> _targetGrids = []; public override void Initialize() { @@ -124,32 +124,38 @@ private void OnScramImplant(EntityUid uid, SubdermalImplantComponent component, private EntityCoordinates? SelectRandomTileInRange(TransformComponent userXform, float radius) { var userCoords = userXform.Coordinates.ToMap(EntityManager, _xform); - var grids = _lookupSystem.GetEntitiesInRange(userCoords, radius).ToList(); - _random.Shuffle(grids); + _targetGrids.Clear(); + _lookupSystem.GetEntitiesInRange(userCoords, radius, _targetGrids); + Entity? targetGrid = null; + + if (_targetGrids.Count == 0) + return null; // Give preference to the grid the entity is currently on. - var idx = grids.FindIndex(grid => grid.Owner == userXform.GridUid); - if (idx != -1) + // 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(userXform.GridUid, out var gridComp)) { - if (_random.Prob(0.66f)) - { - (grids[0], grids[idx]) = (grids[idx], grids[0]); - } - else + var userGrid = new Entity(userXform.GridUid.Value, gridComp); + if (_random.Prob(0.5f)) { - (grids[^1], grids[idx]) = (grids[idx], grids[^1]); + _targetGrids.Remove(userGrid); + targetGrid = userGrid; } } + if (targetGrid == null) + targetGrid = _random.GetRandom().PickAndTake(_targetGrids); + EntityCoordinates? targetCoords = null; - foreach (var grid in grids) + do { var valid = false; var range = (float) Math.Sqrt(radius); var box = Box2.CenteredAround(userCoords.Position, new Vector2(range, range)); - var tilesInRange = _mapSystem.GetTilesEnumerator(grid.Owner, grid.Comp, box, false); + var tilesInRange = _mapSystem.GetTilesEnumerator(targetGrid.Value.Owner, targetGrid.Value.Comp, box, false); var tileList = new ValueList(); while (tilesInRange.MoveNext(out var tile)) @@ -161,7 +167,8 @@ private void OnScramImplant(EntityUid uid, SubdermalImplantComponent component, { var tile = tileList.RemoveSwap(_random.Next(tileList.Count)); valid = true; - foreach (var entity in _mapSystem.GetAnchoredEntities(grid.Owner, grid.Comp, tile)) + foreach (var entity in _mapSystem.GetAnchoredEntities(targetGrid.Value.Owner, targetGrid.Value.Comp, + tile)) { if (!_physicsQuery.TryGetComponent(entity, out var body)) continue; @@ -177,14 +184,17 @@ private void OnScramImplant(EntityUid uid, SubdermalImplantComponent component, if (valid) { - targetCoords = new EntityCoordinates(grid.Owner, _mapSystem.TileCenterToVector(grid, tile)); + 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; - } + + targetGrid = _random.GetRandom().PickAndTake(_targetGrids); + } while (true); return targetCoords; }