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); } }
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); }
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)); }
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); } }
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); } }
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); } } }
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); } }
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 }
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); }); }
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)); }
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); } }
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)); } }