From: alexalexmax <149889301+alexalexmax@users.noreply.github.com> Date: Fri, 12 Dec 2025 11:14:45 +0000 (-0800) Subject: ScramOnTrigger teleportation logic rewrite (#41808) X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=cbbf978408117385555d96863cf16a67f36732dd;p=space-station-14.git ScramOnTrigger teleportation logic rewrite (#41808) * shuffle logic and remove arbitrary line * actually fix radius for real * code comment for clarity * rewrote SelectRandomTileInRange * Update Content.Shared/Trigger/Systems/ScramOnTriggerSystem.cs Co-authored-by: āda * Update Content.Shared/Trigger/Systems/ScramOnTriggerSystem.cs Co-authored-by: āda * i love helper methods! * remove unused dependencies * check collisiongroup of teleporting entity * remove unused dep. * unused query and init --------- Co-authored-by: āda Co-authored-by: ScarKy0 --- diff --git a/Content.Shared/Trigger/Systems/ScramOnTriggerSystem.cs b/Content.Shared/Trigger/Systems/ScramOnTriggerSystem.cs index a2c280d0d5..eded400712 100644 --- a/Content.Shared/Trigger/Systems/ScramOnTriggerSystem.cs +++ b/Content.Shared/Trigger/Systems/ScramOnTriggerSystem.cs @@ -1,15 +1,12 @@ -using System.Numerics; +using Content.Shared.Maps; 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; @@ -17,22 +14,11 @@ namespace Content.Shared.Trigger.Systems; public sealed class ScramOnTriggerSystem : XOnTriggerSystem { [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 _physicsQuery; - private HashSet> _targetGrids = new(); - - public override void Initialize() - { - base.Initialize(); - - _physicsQuery = GetEntityQuery(); - } + [Dependency] private readonly TurfSystem _turfSystem = default!; protected override void OnTrigger(Entity ent, EntityUid target, ref TriggerEvent args) { @@ -51,8 +37,7 @@ public sealed class ScramOnTriggerSystem : XOnTriggerSystem + /// Method to find a random empty tile within a certain radius. Will not select off-grid tiles. Returns + /// null if no tile is found within a certain number of tries. + /// + /// Trends towards the outer radius. Compensates for small grids. + private EntityCoordinates? SelectRandomTileInRange(EntityUid uid, float radius, int tries = 40, PhysicsComponent? physicsComponent = null) { - var userCoords = _transform.ToMapCoordinates(userXform.Coordinates); - _targetGrids.Clear(); - _lookup.GetEntitiesInRange(userCoords, radius, _targetGrids); - Entity? 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(userXform.GridUid, out var gridComp)) - { - var userGrid = new Entity(userXform.GridUid.Value, gridComp); - if (_random.Prob(0.5f)) - { - _targetGrids.Remove(userGrid); - targetGrid = userGrid; - } - } - - if (targetGrid == null) - targetGrid = _random.GetRandom().PickAndTake(_targetGrids); - + var userCoords = Transform(uid).Coordinates; 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(); - - 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 (!Resolve(uid, ref physicsComponent)) + return targetCoords; - 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); + for (var i = 0; i < tries; i++) + { + // distance = r * sq(x) * i + // r = the radius of the search area. + // sq(x) = the square root of [0 - 1]. Gives a number trending to the + // upper range of [0, 1] so that you tend to teleport further. + // i = A percentage based on the current try count, which results in each + // subsequent try landing closer and closer towards the entity. + // Beneficial for smaller maps, especially when the radius is large. + var distance = radius * MathF.Sqrt(_random.NextFloat()) * (1 - (float)i / tries); + + // We then offset the user coords from a random angle * distance + var tempTargetCoords = userCoords.Offset(_random.NextAngle().ToVec() * distance); + + if (!_turfSystem.TryGetTileRef(tempTargetCoords, out var tileRef) + || _turfSystem.IsSpace(tileRef.Value) + || _turfSystem.IsTileBlocked(tileRef.Value, (CollisionGroup)physicsComponent.CollisionMask)) + continue; + + targetCoords = tempTargetCoords; + break; + } return targetCoords; }