예제 #1
0
        public override IEnumerable <Node> GetReachableNodes(TransformComponent xform,
                                                             EntityQuery <NodeContainerComponent> nodeQuery,
                                                             EntityQuery <TransformComponent> xformQuery,
                                                             IMapGrid?grid,
                                                             IEntityManager entMan)
        {
            if (!xform.Anchored || grid == null)
            {
                yield break;
            }

            var gridIndex = grid.TileIndicesFor(xform.Coordinates);

            foreach (var node in NodeHelpers.GetNodesInTile(nodeQuery, grid, gridIndex))
            {
                if (node is PortablePipeNode)
                {
                    yield return(node);
                }
            }

            foreach (var node in base.GetReachableNodes(xform, nodeQuery, xformQuery, grid, entMan))
            {
                yield return(node);
            }
        }
예제 #2
0
        public static EntityCoordinates AlignWithClosestGridTile(this EntityCoordinates coordinates, float searchBoxSize = 1.5f, IEntityManager?entityManager = null, IMapManager?mapManager = null)
        {
            var coords = coordinates;

            entityManager ??= IoCManager.Resolve <IEntityManager>();
            mapManager ??= IoCManager.Resolve <IMapManager>();

            var gridId = coords.GetGridId(entityManager);

            if (!gridId.IsValid() || !mapManager.GridExists(gridId))
            {
                var mapCoords = coords.ToMap(entityManager);

                // create a box around the cursor
                var gridSearchBox = Box2.UnitCentered.Scale(searchBoxSize).Translated(mapCoords.Position);

                // find grids in search box
                var gridsInArea = mapManager.FindGridsIntersecting(mapCoords.MapId, gridSearchBox);

                // find closest grid intersecting our search box.
                IMapGrid?closest   = null;
                var      distance  = float.PositiveInfinity;
                var      intersect = new Box2();
                foreach (var grid in gridsInArea)
                {
                    // TODO: Use CollisionManager to get nearest edge.

                    // figure out closest intersect
                    var gridIntersect = gridSearchBox.Intersect(grid.WorldBounds);
                    var gridDist      = (gridIntersect.Center - mapCoords.Position).LengthSquared;

                    if (gridDist >= distance)
                    {
                        continue;
                    }

                    distance  = gridDist;
                    closest   = grid;
                    intersect = gridIntersect;
                }

                if (closest != null) // stick to existing grid
                {
                    // round to nearest cardinal dir
                    var normal = mapCoords.Position - intersect.Center;

                    // round coords to center of tile
                    var tileIndices     = closest.WorldToTile(intersect.Center);
                    var tileCenterWorld = closest.GridTileToWorldPos(tileIndices);

                    // move mouse one tile out along normal
                    var newTilePos = tileCenterWorld + normal * closest.TileSize;

                    coords = new EntityCoordinates(closest.GridEntityId, closest.WorldToLocal(newTilePos));
                }
                //else free place
            }

            return(coords);
        }
예제 #3
0
        public override void AlignPlacementMode(ScreenCoordinates mouseScreen)
        {
            MouseCoords = ScreenToCursorGrid(mouseScreen);

            var gridId = MouseCoords.GetGridId(pManager.EntityManager);

            SnapSize = 1f;
            if (gridId.IsValid())
            {
                Grid     = pManager.MapManager.GetGrid(gridId);
                SnapSize = Grid.TileSize; //Find snap size for the grid.
            }
            else
            {
                Grid = null;
            }

            GridDistancing = SnapSize;

            var mouseLocal = new Vector2( //Round local coordinates onto the snap grid
                (float)(MathF.Round((MouseCoords.Position.X / SnapSize - 0.5f), MidpointRounding.AwayFromZero) + 0.5) * SnapSize,
                (float)(MathF.Round((MouseCoords.Position.Y / SnapSize - 0.5f), MidpointRounding.AwayFromZero) + 0.5) * SnapSize);

            //Adjust mouseCoords to new calculated position
            MouseCoords = new EntityCoordinates(MouseCoords.EntityId, mouseLocal + new Vector2(pManager.PlacementOffset.X, pManager.PlacementOffset.Y));
        }
예제 #4
0
        public override IEnumerable <Node> GetReachableNodes(TransformComponent xform,
                                                             EntityQuery <NodeContainerComponent> nodeQuery,
                                                             EntityQuery <TransformComponent> xformQuery,
                                                             IMapGrid?grid,
                                                             IEntityManager entMan)
        {
            if (!xform.Anchored || grid == null)
            {
                yield break;
            }

            var gridIndex = grid.TileIndicesFor(xform.Coordinates);

            var dir       = xform.LocalRotation.GetDir();
            var targetIdx = gridIndex.Offset(dir);

            foreach (var node in NodeHelpers.GetNodesInTile(nodeQuery, grid, targetIdx))
            {
                if (node is CableTerminalPortNode)
                {
                    yield return(node);
                }
            }

            foreach (var node in base.GetReachableNodes(xform, nodeQuery, xformQuery, grid, entMan))
            {
                yield return(node);
            }
        }
예제 #5
0
        internal override void CalculateNewSprite(IMapGrid?grid)
        {
            base.CalculateNewSprite(grid);

            var(cornerNE, cornerNW, cornerSW, cornerSE) = CalculateCornerFill(grid);

            if (Sprite != null)
            {
                Sprite.LayerSetState(ReinforcedCornerLayers.NE, $"{_reinforcedStateBase}{(int) cornerNE}");
                Sprite.LayerSetState(ReinforcedCornerLayers.SE, $"{_reinforcedStateBase}{(int) cornerSE}");
                Sprite.LayerSetState(ReinforcedCornerLayers.SW, $"{_reinforcedStateBase}{(int) cornerSW}");
                Sprite.LayerSetState(ReinforcedCornerLayers.NW, $"{_reinforcedStateBase}{(int) cornerNW}");
            }
        }
        public override IEnumerable <Node> GetReachableNodes(TransformComponent xform,
                                                             EntityQuery <NodeContainerComponent> nodeQuery,
                                                             EntityQuery <TransformComponent> xformQuery,
                                                             IMapGrid?grid,
                                                             IEntityManager entMan)
        {
            if (!nodeQuery.TryGetComponent(CableEntity, out var nodeContainer))
            {
                yield break;
            }

            if (nodeContainer.TryGetNode(NodeName, out Node? node))
            {
                yield return(node);
            }
        }
예제 #7
0
        public override IEnumerable <Node> GetReachableNodes(TransformComponent xform,
                                                             EntityQuery <NodeContainerComponent> nodeQuery,
                                                             EntityQuery <TransformComponent> xformQuery,
                                                             IMapGrid?grid,
                                                             IEntityManager entMan)
        {
            if (!xform.Anchored || grid == null)
            {
                yield break;
            }

            var gridIndex = grid.TileIndicesFor(xform.Coordinates);

            foreach (var(_, node) in NodeHelpers.GetCardinalNeighborNodes(nodeQuery, grid, gridIndex))
            {
                if (node != this)
                {
                    yield return(node);
                }
            }
        }
예제 #8
0
        public override IEnumerable <Node> GetReachableNodes(TransformComponent xform,
                                                             EntityQuery <NodeContainerComponent> nodeQuery,
                                                             EntityQuery <TransformComponent> xformQuery,
                                                             IMapGrid?grid,
                                                             IEntityManager entMan)
        {
            if (!xform.Anchored || grid == null)
            {
                yield break;
            }

            var gridIndex = grid.TileIndicesFor(xform.Coordinates);

            // While we go over adjacent nodes, we build a list of blocked directions due to
            // incoming or outgoing wire terminals.
            var terminalDirs = 0;
            List <(Direction, Node)> nodeDirs = new();

            foreach (var(dir, node) in NodeHelpers.GetCardinalNeighborNodes(nodeQuery, grid, gridIndex))
            {
                if (node is CableNode && node != this)
                {
                    nodeDirs.Add((dir, node));
                }

                if (node is CableDeviceNode && dir == Direction.Invalid)
                {
                    // device on same tile
                    nodeDirs.Add((Direction.Invalid, node));
                }

                if (node is CableTerminalNode)
                {
                    if (dir == Direction.Invalid)
                    {
                        // On own tile, block direction it faces
                        terminalDirs |= 1 << (int)xformQuery.GetComponent(node.Owner).LocalRotation.GetCardinalDir();
                    }
                    else
                    {
                        var terminalDir = xformQuery.GetComponent(node.Owner).LocalRotation.GetCardinalDir();
                        if (terminalDir.GetOpposite() == dir)
                        {
                            // Target tile has a terminal towards us, block the direction.
                            terminalDirs |= 1 << (int)dir;
                            break;
                        }
                    }
                }
            }

            foreach (var(dir, node) in nodeDirs)
            {
                // If there is a wire terminal connecting across this direction, skip the node.
                if (dir != Direction.Invalid && (terminalDirs & (1 << (int)dir)) != 0)
                {
                    continue;
                }

                yield return(node);
            }
        }
예제 #9
0
        public override void AlignPlacementMode(ScreenCoordinates mouseScreen)
        {
            const float SearchBoxSize = 1.5f; // size of search box in meters

            MouseCoords = ScreenToCursorGrid(mouseScreen);

            var mapGrid = pManager.MapManager.GetGrid(MouseCoords.GridID);

            if (mapGrid.IsDefaultGrid)
            {
                // check if we are on an edge of a grid
                // create a box around the cursor
                DebugTools.Assert(mapGrid.WorldPosition == Vector2.Zero); // assert that LocalPos == WorldPos
                var gridSearchBox = Box2.UnitCentered.Scale(SearchBoxSize).Translated(MouseCoords.Position);

                // find grids in search box
                var gridsInArea = pManager.MapManager.FindGridsIntersecting(mapGrid.ParentMapId, gridSearchBox);

                // find closest grid intersecting our search box.
                IMapGrid?closest   = null;
                var      distance  = float.PositiveInfinity;
                var      intersect = new Box2();
                foreach (var grid in gridsInArea)
                {
                    // figure out closest intersect
                    var gridIntersect = gridSearchBox.Intersect(grid.WorldBounds);
                    var gridDist      = (gridIntersect.Center - MouseCoords.Position).LengthSquared;

                    if (gridDist >= distance)
                    {
                        continue;
                    }

                    distance  = gridDist;
                    closest   = grid;
                    intersect = gridIntersect;
                }

                if (closest != null) // stick to existing grid
                {
                    // round to nearest cardinal dir
                    var normal = new Angle(MouseCoords.Position - intersect.Center).GetCardinalDir().ToVec();

                    // round coords to center of tile
                    var tileIndices     = closest.WorldToTile(intersect.Center);
                    var tileCenterWorld = closest.GridTileToWorldPos(tileIndices);

                    // move mouse one tile out along normal
                    var newTilePos = tileCenterWorld + normal * closest.TileSize;
                    MouseCoords = new GridCoordinates(closest.WorldToLocal(newTilePos), closest.Index);
                }
                //else free place
            }


            CurrentTile = mapGrid.GetTileRef(MouseCoords);
            float tileSize = mapGrid.TileSize; //convert from ushort to float

            GridDistancing = tileSize;

            if (pManager.CurrentPermission !.IsTile)
            {
                if (!mapGrid.IsDefaultGrid)
                {
                    MouseCoords = new GridCoordinates(CurrentTile.X + tileSize / 2,
                                                      CurrentTile.Y + tileSize / 2,
                                                      MouseCoords.GridID);
                }
                // else we don't modify coords
            }
예제 #10
0
        public async Task TestGridsCollide()
        {
            var server = StartServer();

            await server.WaitIdleAsync();

            var mapManager = server.ResolveDependency <IMapManager>();
            var entManager = server.ResolveDependency <IEntityManager>();

            MapId            mapId;
            IMapGrid?        gridId1  = null;
            IMapGrid?        gridId2  = null;
            PhysicsComponent?physics1 = null;
            PhysicsComponent?physics2 = null;
            EntityUid?       gridEnt1;
            EntityUid?       gridEnt2;

            await server.WaitPost(() =>
            {
                mapId    = mapManager.CreateMap();
                gridId1  = mapManager.CreateGrid(mapId);
                gridId2  = mapManager.CreateGrid(mapId);
                gridEnt1 = gridId1.GridEntityId;
                gridEnt2 = gridId2.GridEntityId;
                physics1 = IoCManager.Resolve <IEntityManager>().GetComponent <PhysicsComponent>(gridEnt1.Value);
                physics2 = IoCManager.Resolve <IEntityManager>().GetComponent <PhysicsComponent>(gridEnt2.Value);
                // Can't collide static bodies and grids (at time of this writing) start as static
                // (given most other games would probably prefer them as static) hence we need to make them dynamic.
                physics1.BodyType = BodyType.Dynamic;
                physics2.BodyType = BodyType.Dynamic;
            });

            await server.WaitRunTicks(1);

            // No tiles set hence should be no collision
            await server.WaitAssertion(() =>
            {
                var edge = physics1?.ContactEdges;

                while (edge != null)
                {
                    Assert.That(edge.Other, Is.Not.EqualTo(physics2));
                    edge = edge.Next;
                }
            });

            await server.WaitAssertion(() =>
            {
                gridId1?.SetTile(new Vector2i(0, 0), new Tile(1));
                gridId2?.SetTile(new Vector2i(0, 0), new Tile(1));
            });

            await server.WaitRunTicks(1);

            await server.WaitAssertion(() =>
            {
                var colliding = false;
                var edge      = physics1?.ContactEdges;

                while (edge != null)
                {
                    if (edge.Other == physics2)
                    {
                        colliding = true;
                        break;
                    }

                    edge = edge.Next;
                }

                Assert.That(colliding);
            });
        }
예제 #11
0
        public override void AlignPlacementMode(ScreenCoordinates mouseScreen)
        {
            const float SearchBoxSize = 1.5f; // size of search box in meters

            MouseCoords = ScreenToCursorGrid(mouseScreen);

            var gridId = MouseCoords.GetGridId(pManager.EntityManager);

            IMapGrid?mapGrid = null;

            if (!gridId.IsValid() || !pManager.MapManager.TryGetGrid(gridId, out mapGrid))
            {
                // create a box around the cursor
                var gridSearchBox = Box2.UnitCentered.Scale(SearchBoxSize).Translated(MouseCoords.Position);

                // find grids in search box
                var gridsInArea = pManager.MapManager.FindGridsIntersecting(MouseCoords.GetMapId(pManager.EntityManager), gridSearchBox);

                // find closest grid intersecting our search box.
                IMapGrid?closest   = null;
                var      distance  = float.PositiveInfinity;
                var      intersect = new Box2();
                foreach (var grid in gridsInArea)
                {
                    // figure out closest intersect
                    var gridIntersect = gridSearchBox.Intersect(grid.WorldBounds);
                    var gridDist      = (gridIntersect.Center - MouseCoords.Position).LengthSquared;

                    if (gridDist >= distance)
                    {
                        continue;
                    }

                    distance  = gridDist;
                    closest   = grid;
                    intersect = gridIntersect;
                }

                if (closest != null) // stick to existing grid
                {
                    // round to nearest cardinal dir
                    var normal = new Angle(MouseCoords.Position - intersect.Center).GetCardinalDir().ToVec();

                    // round coords to center of tile
                    var tileIndices     = closest.WorldToTile(intersect.Center);
                    var tileCenterWorld = closest.GridTileToWorldPos(tileIndices);

                    // move mouse one tile out along normal
                    var newTilePos = tileCenterWorld + normal * closest.TileSize;

                    MouseCoords = new EntityCoordinates(closest.GridEntityId, closest.WorldToLocal(newTilePos));
                    mapGrid     = closest;
                }
                //else free place
            }

            if (mapGrid == null)
            {
                return;
            }

            CurrentTile = mapGrid.GetTileRef(MouseCoords);
            float tileSize = mapGrid.TileSize; //convert from ushort to float

            GridDistancing = tileSize;

            if (pManager.CurrentPermission !.IsTile)
            {
                MouseCoords = new EntityCoordinates(MouseCoords.EntityId, (CurrentTile.X + tileSize / 2,
                                                                           CurrentTile.Y + tileSize / 2));
            }
예제 #12
0
        public bool MoveNext([NotNullWhen(true)] out IMapGrid?grid)
        {
            while (true)
            {
                if (!_enumerator.MoveNext())
                {
                    grid = null;
                    return(false);
                }

                var(_, nextGrid) = _enumerator.Current;

                if (nextGrid.ParentMapId != _mapId)
                {
                    continue;
                }

                var xformComp  = _entityManager.GetComponent <TransformComponent>(nextGrid.GridEntityId);
                var invMatrix3 = xformComp.InvWorldMatrix;
                var localAABB  = invMatrix3.TransformBox(_worldAABB);

                if (!localAABB.Intersects(nextGrid.LocalBounds))
                {
                    continue;
                }

                var intersects = false;

                if (_entityManager.HasComponent <PhysicsComponent>(nextGrid.GridEntityId))
                {
                    nextGrid.GetLocalMapChunks(localAABB, out var enumerator);

                    if (!_approx)
                    {
                        var(worldPos, worldRot) = xformComp.GetWorldPositionRotation();

                        var transform = new Transform(worldPos, worldRot);

                        while (!intersects && enumerator.MoveNext(out var chunk))
                        {
                            foreach (var fixture in chunk.Fixtures)
                            {
                                for (var i = 0; i < fixture.Shape.ChildCount; i++)
                                {
                                    if (!fixture.Shape.ComputeAABB(transform, i).Intersects(_worldAABB))
                                    {
                                        continue;
                                    }

                                    intersects = true;
                                    break;
                                }

                                if (intersects)
                                {
                                    break;
                                }
                            }
                        }
                    }
                    else
                    {
                        intersects = enumerator.MoveNext(out _);
                    }
                }

                if (!intersects && nextGrid.ChunkCount == 0 && !_worldAABB.Contains(xformComp.WorldPosition))
                {
                    continue;
                }

                grid = nextGrid;
                return(true);
            }
        }
예제 #13
0
        private void PlaceNewTile(ushort tileType, MapId mapId, Vector2 position)
        {
            // tile can snap up to 0.75m away from grid
            var gridSearchBox = new Box2(-0.5f, -0.5f, 0.5f, 0.5f)
                                .Scale(1.5f)
                                .Translated(position);

            var gridsInArea = _mapManager.FindGridsIntersecting(mapId, gridSearchBox);

            IMapGrid?closest   = null;
            float    distance  = float.PositiveInfinity;
            Box2     intersect = new Box2();

            foreach (var grid in gridsInArea)
            {
                // figure out closest intersect
                var gridIntersect = gridSearchBox.Intersect(grid.WorldBounds);
                var gridDist      = (gridIntersect.Center - position).LengthSquared;

                if (gridDist >= distance)
                {
                    continue;
                }

                distance  = gridDist;
                closest   = grid;
                intersect = gridIntersect;
            }

            if (closest != null) // stick to existing grid
            {
                // round to nearest cardinal dir
                var normal = new Angle(position - intersect.Center).GetCardinalDir().ToVec();

                // round coords to center of tile
                var tileIndices     = closest.WorldToTile(intersect.Center);
                var tileCenterWorld = closest.GridTileToWorldPos(tileIndices);

                // move mouse one tile out along normal
                var newTilePos = tileCenterWorld + normal * closest.TileSize;

                // you can always remove a tile
                if (Tile.Empty.TypeId != tileType)
                {
                    var tileBounds = Box2.UnitCentered.Scale(closest.TileSize).Translated(newTilePos);

                    var collideCount = _mapManager.FindGridsIntersecting(mapId, tileBounds).Count();

                    // prevent placing a tile if it overlaps more than one grid
                    if (collideCount > 1)
                    {
                        return;
                    }
                }

                var pos = closest.WorldToTile(position);
                closest.SetTile(pos, new Tile(tileType));
            }
            else // create a new grid
            {
                var newGrid = _mapManager.CreateGrid(mapId);
                newGrid.WorldPosition = position + (newGrid.TileSize / 2f); // assume bottom left tile origin
                var tilePos = newGrid.WorldToTile(position);
                newGrid.SetTile(tilePos, new Tile(tileType));
            }
        }