diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs index f30cab253a0ec4..8f0078010b625e 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs @@ -70,11 +70,11 @@ public sealed partial class ShuttleSystem private readonly HashSet _lookupEnts = new(); private readonly HashSet _immuneEnts = new(); + private readonly HashSet> _noFtls = new(); private EntityQuery _bodyQuery; private EntityQuery _buckleQuery; - private EntityQuery _beaconQuery; - private EntityQuery _ghostQuery; + private EntityQuery _immuneQuery; private EntityQuery _physicsQuery; private EntityQuery _statusQuery; private EntityQuery _xformQuery; @@ -86,8 +86,7 @@ private void InitializeFTL() _bodyQuery = GetEntityQuery(); _buckleQuery = GetEntityQuery(); - _beaconQuery = GetEntityQuery(); - _ghostQuery = GetEntityQuery(); + _immuneQuery = GetEntityQuery(); _physicsQuery = GetEntityQuery(); _statusQuery = GetEntityQuery(); _xformQuery = GetEntityQuery(); @@ -102,7 +101,7 @@ private void InitializeFTL() private void OnFtlShutdown(Entity ent, ref ComponentShutdown args) { - Del(ent.Comp.VisualizerEntity); + QueueDel(ent.Comp.VisualizerEntity); ent.Comp.VisualizerEntity = null; } @@ -404,7 +403,12 @@ private void UpdateFTLStarting(Entity entity) // Offset the start by buffer range just to avoid overlap. var ftlStart = new EntityCoordinates(ftlMap, new Vector2(_index + width / 2f, 0f) - shuttleCenter); + // Store the matrix for the grid prior to movement. This means any entities we need to leave behind we can make sure their positions are updated. + // Setting the entity to map directly may run grid traversal (at least at time of writing this). + var oldMapUid = xform.MapUid; + var oldGridMatrix = _transform.GetWorldMatrix(xform); _transform.SetCoordinates(entity.Owner, ftlStart); + LeaveNoFTLBehind((entity.Owner, xform), oldGridMatrix, oldMapUid); // Reset rotation so they always face the same direction. xform.LocalRotation = Angle.Zero; @@ -476,6 +480,9 @@ private void UpdateFTLArriving(Entity entity) MapId mapId; + QueueDel(entity.Comp1.VisualizerEntity); + entity.Comp1.VisualizerEntity = null; + if (!Exists(entity.Comp1.TargetCoordinates.EntityId)) { // Uhh good luck @@ -628,6 +635,31 @@ private void DoTheDinosaur(TransformComponent xform) } } + private void LeaveNoFTLBehind(Entity grid, Matrix3x2 oldGridMatrix, EntityUid? oldMapUid) + { + if (oldMapUid == null) + return; + + _noFtls.Clear(); + var oldGridRotation = oldGridMatrix.Rotation(); + _lookup.GetGridEntities(grid.Owner, _noFtls); + + foreach (var childUid in _noFtls) + { + if (!_xformQuery.TryComp(childUid, out var childXform)) + continue; + + // If we're not parented directly to the grid the matrix may be wrong. + var relative = _physics.GetRelativePhysicsTransform(childUid.Owner, (grid.Owner, grid.Comp)); + + _transform.SetCoordinates( + childUid, + childXform, + new EntityCoordinates(oldMapUid.Value, + Vector2.Transform(relative.Position, oldGridMatrix)), rotation: relative.Quaternion2D.Angle + oldGridRotation); + } + } + private void KnockOverKids(TransformComponent xform, ref ValueList toKnock) { // Not recursive because probably not necessary? If we need it to be that's why this method is separate. @@ -946,7 +978,8 @@ private void Smimsh(EntityUid uid, FixturesComponent? manager = null, MapGridCom _biomes.ReserveTiles(xform.MapUid.Value, aabb, tileSet); _lookupEnts.Clear(); _immuneEnts.Clear(); - _lookup.GetEntitiesIntersecting(xform.MapUid.Value, aabb, _lookupEnts, LookupFlags.Uncontained); + // TODO: Ideally we'd query first BEFORE moving grid but needs adjustments above. + _lookup.GetEntitiesIntersecting(xform.MapID, fixture.Shape, transform, _lookupEnts, LookupFlags.Uncontained); foreach (var ent in _lookupEnts) { @@ -955,7 +988,13 @@ private void Smimsh(EntityUid uid, FixturesComponent? manager = null, MapGridCom continue; } - if (_ghostQuery.HasComponent(ent) || _beaconQuery.HasComponent(ent)) + // If it's on our grid ignore it. + if (!_xformQuery.TryComp(ent, out var childXform) || childXform.GridUid == uid) + { + continue; + } + + if (_immuneQuery.HasComponent(ent)) { continue; } @@ -969,9 +1008,6 @@ private void Smimsh(EntityUid uid, FixturesComponent? manager = null, MapGridCom continue; } - if (HasComp(ent)) - continue; - QueueDel(ent); } } diff --git a/Content.Shared/Shuttles/Components/FTLSmashImmuneComponent.cs b/Content.Shared/Shuttles/Components/FTLSmashImmuneComponent.cs new file mode 100644 index 00000000000000..9ed7ee05a51ab9 --- /dev/null +++ b/Content.Shared/Shuttles/Components/FTLSmashImmuneComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Shuttles.Components; + +/// +/// Makes the entity immune to FTL arrival landing AKA smimsh. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class FTLSmashImmuneComponent : Component; diff --git a/Content.Shared/Shuttles/Components/NoFTLComponent.cs b/Content.Shared/Shuttles/Components/NoFTLComponent.cs new file mode 100644 index 00000000000000..d48ba33bbacbd4 --- /dev/null +++ b/Content.Shared/Shuttles/Components/NoFTLComponent.cs @@ -0,0 +1,12 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Shuttles.Components; + +/// +/// Prevents the attached entity from taking FTL. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class NoFTLComponent : Component +{ + +} diff --git a/Resources/Prototypes/Entities/Markers/shuttle.yml b/Resources/Prototypes/Entities/Markers/shuttle.yml index 0e9117951ce114..66e2bc39b7f4c3 100644 --- a/Resources/Prototypes/Entities/Markers/shuttle.yml +++ b/Resources/Prototypes/Entities/Markers/shuttle.yml @@ -3,6 +3,7 @@ parent: MarkerBase name: FTL point components: + - type: FTLSmashImmune - type: FTLBeacon - type: Sprite state: pink diff --git a/Resources/Prototypes/Entities/Mobs/Player/observer.yml b/Resources/Prototypes/Entities/Mobs/Player/observer.yml index 5ceac9e773e06a..c02629c4d6fd81 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/observer.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/observer.yml @@ -8,6 +8,7 @@ noRot: true overrideContainerOcclusion: true # Always show up regardless of where they're contained. drawdepth: Ghosts + - type: FTLSmashImmune - type: CargoSellBlacklist - type: MovementSpeedModifier baseSprintSpeed: 12 diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index d7d0cd0e65974e..601fa1f5c1bc79 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml @@ -146,7 +146,7 @@ state: std_mod - type: SiliconLawProvider laws: PaladinLawset - + - type: entity id: LiveLetLiveCircuitBoard parent: BaseElectronics @@ -158,7 +158,7 @@ state: std_mod - type: SiliconLawProvider laws: LiveLetLiveLaws - + - type: entity id: StationEfficiencyCircuitBoard parent: BaseElectronics @@ -182,7 +182,7 @@ state: std_mod - type: SiliconLawProvider laws: RobocopLawset - + - type: entity id: OverlordCircuitBoard parent: BaseElectronics @@ -194,7 +194,7 @@ state: std_mod - type: SiliconLawProvider laws: OverlordLawset - + - type: entity id: DungeonMasterCircuitBoard parent: BaseElectronics @@ -230,7 +230,7 @@ state: std_mod - type: SiliconLawProvider laws: AntimovLawset - + - type: entity id: NutimovCircuitBoard parent: BaseElectronics @@ -376,6 +376,7 @@ noSpawn: true suffix: DO NOT MAP components: + - type: NoFTL - type: WarpPoint follow: true - type: Eye