using System.Collections.Generic; using System.Numerics; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Maths; using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Robust.Server.Physics; public sealed partial class GridFixtureSystem { /* * Something to keep in mind is that they rotate around the origin of a tile (e.g. 0,0), so a rotation of -90 degrees * moves the origin to 0, -1. */ /// /// Merges GridB into GridA. /// /// Origin of GridB relative to GridA /// Rotation to apply to GridB when being merged. /// Note that the rotation is applied before the offset so the offset itself will be rotated. public void Merge( EntityUid gridAUid, EntityUid gridBUid, Vector2i offset, Angle rotation, MapGridComponent? gridA = null, MapGridComponent? gridB = null, TransformComponent? xformA = null, TransformComponent? xformB = null) { var matrix = Matrix3Helpers.CreateTransform(offset, rotation); Merge(gridAUid, gridBUid, matrix, gridA, gridB, xformA, xformB); } /// /// Merges GridB into GridA. /// /// Origin of GridB relative to GridA /// Matrix to apply to gridB when being merged. /// Note that rotation is applied first and then offset so the offset itself will be rotated. public void Merge( EntityUid gridAUid, EntityUid gridBUid, Matrix3x2 matrix, MapGridComponent? gridA = null, MapGridComponent? gridB = null, TransformComponent? xformA = null, TransformComponent? xformB = null) { if (!Resolve(gridAUid, ref gridA, ref xformA)) return; if (!Resolve(gridBUid, ref gridB, ref xformB)) return; var sw = new Stopwatch(); var tiles = new List<(Vector2i Indices, Tile Tile)>(); foreach (var tileRef in _maps.GetAllTiles(gridBUid, gridB)) { var offsetTile = Vector2.Transform(new Vector2(tileRef.GridIndices.X, tileRef.GridIndices.Y) + gridA.TileSizeHalfVector, matrix); tiles.Add((offsetTile.Floored(), tileRef.Tile)); } _maps.SetTiles(gridAUid, gridA, tiles); var rotationDiff = matrix.Rotation(); foreach (var tileRef in _maps.GetAllTiles(gridBUid, gridB)) { var chunkOrigin = SharedMapSystem.GetChunkIndices(tileRef.GridIndices, gridB.ChunkSize); if (!_maps.TryGetChunk(gridBUid, gridB, chunkOrigin, out var chunk)) { continue; } var chunkLocalTile = SharedMapSystem.GetChunkRelative(tileRef.GridIndices, gridB.ChunkSize); var snapgrid = chunk.GetSnapGrid((ushort) chunkLocalTile.X, (ushort) chunkLocalTile.Y); if (snapgrid == null || snapgrid.Count == 0) continue; var offsetTile = Vector2.Transform(new Vector2(tileRef.GridIndices.X, tileRef.GridIndices.Y) + gridA.TileSizeHalfVector, matrix); var tileIndex = offsetTile.Floored(); for (var j = snapgrid.Count - 1; j >= 0; j--) { var ent = snapgrid[j]; var xform = _xformQuery.GetComponent(ent); _xformSystem.ReAnchor(ent, xform, gridB, gridA, tileRef.GridIndices, tileIndex, gridBUid, gridAUid, xformB, xformA, rotationDiff); DebugTools.Assert(xform.ParentUid == gridAUid); } DebugTools.Assert(snapgrid.Count == 0); } foreach (var tileRef in _maps.GetAllTiles(gridBUid, gridB)) { var bounds = _lookup.GetLocalBounds(tileRef.GridIndices, gridB.TileSize); _entSet.Clear(); _lookup.GetLocalEntitiesIntersecting(gridBUid, bounds, _entSet, LookupFlags.All | ~LookupFlags.Contained | LookupFlags.Approximate); foreach (var ent in _entSet) { // Consider centre of entity position maybe? var entXform = _xformQuery.GetComponent(ent); if (entXform.ParentUid != gridBUid || !bounds.Contains(entXform.LocalPosition)) continue; var newPos = Vector2.Transform(entXform.LocalPosition, matrix); _xformSystem.SetCoordinates(ent, entXform, new EntityCoordinates(gridAUid, newPos), entXform.LocalRotation + rotationDiff, oldParent: xformB, newParent: xformA); } } DebugTools.Assert(xformB.ChildCount == 0); Del(gridBUid); Log.Debug($"Merged grids in {sw.Elapsed.TotalMilliseconds}ms"); } }