private HashSet <GridTileLookupNode> GetOrCreateNodes(EntityCoordinates coordinates, Box2 box) { var results = new HashSet <GridTileLookupNode>(); foreach (var grid in _mapManager.FindGridsIntersecting(_mapManager.GetGrid(coordinates.GetGridId(EntityManager)).ParentMapId, box)) { foreach (var tile in grid.GetTilesIntersecting(box)) { results.Add(GetOrCreateNode(grid.Index, tile.GridIndices)); } } return(results); }
public override void Update(float frameTime) { AccumulatedFrameTime += frameTime; _updateCooldown = 1 / _configManager.GetCVar <float>("net.atmosdbgoverlaytickrate"); if (AccumulatedFrameTime < _updateCooldown) { return; } // This is the timer from GasTileOverlaySystem AccumulatedFrameTime -= _updateCooldown; var currentTick = _gameTiming.CurTick; // Now we'll go through each player, then through each chunk in range of that player checking if the player is still in range // If they are, check if they need the new data to send (i.e. if there's an overlay for the gas). // Afterwards we reset all the chunk data for the next time we tick. foreach (var session in PlayerObservers) { if (session.AttachedEntity == null) { continue; } var entity = session.AttachedEntity; var worldBounds = Box2.CenteredAround(entity.Transform.WorldPosition, new Vector2(LocalViewRange, LocalViewRange)); foreach (var grid in _mapManager.FindGridsIntersecting(entity.Transform.MapID, worldBounds)) { if (!_entityManager.TryGetEntity(grid.GridEntityId, out var gridEnt)) { continue; } if (!gridEnt.TryGetComponent <GridAtmosphereComponent>(out var gam)) { continue; } var entityTile = grid.GetTileRef(entity.Transform.Coordinates).GridIndices; var baseTile = new MapIndices(entityTile.X - (LocalViewRange / 2), entityTile.Y - (LocalViewRange / 2)); var debugOverlayContent = new AtmosDebugOverlayData[LocalViewRange * LocalViewRange]; var index = 0; for (var y = 0; y < LocalViewRange; y++) { for (var x = 0; x < LocalViewRange; x++) { var mapIndices = new MapIndices(baseTile.X + x, baseTile.Y + y); debugOverlayContent[index++] = ConvertTileToData(gam.GetTile(mapIndices)); } } RaiseNetworkEvent(new AtmosDebugOverlayMessage(grid.Index, baseTile, debugOverlayContent), session.ConnectedClient); } } }
protected override void Draw(DrawingHandleBase handle, OverlaySpace overlay) { var drawHandle = (DrawingHandleWorld)handle; var mapId = _eyeManager.CurrentMap; var eye = _eyeManager.CurrentEye; var worldBounds = Box2.CenteredAround(eye.Position.Position, _clyde.ScreenSize / (float)EyeManager.PixelsPerMeter * eye.Zoom); foreach (var mapGrid in _mapManager.FindGridsIntersecting(mapId, worldBounds)) { if (!_gasTileOverlaySystem.HasData(mapGrid.Index)) { continue; } var gridBounds = new Box2(mapGrid.WorldToLocal(worldBounds.BottomLeft), mapGrid.WorldToLocal(worldBounds.TopRight)); foreach (var tile in mapGrid.GetTilesIntersecting(gridBounds)) { foreach (var(texture, color) in _gasTileOverlaySystem.GetOverlays(mapGrid.Index, tile.GridIndices)) { drawHandle.DrawTexture(texture, mapGrid.LocalToWorld(new Vector2(tile.X, tile.Y)), color); } } } }
public bool CanRecallShuttle(EntityUid?uid, [NotNullWhen(false)] out string?reason, TransformComponent?xform = null) { reason = null; if (!TryComp <IMapGridComponent>(uid, out var grid) || !Resolve(uid.Value, ref xform)) { return(true); } var bounds = grid.Grid.WorldAABB.Enlarged(ShuttleRecallRange); var bodyQuery = GetEntityQuery <PhysicsComponent>(); foreach (var other in _mapManager.FindGridsIntersecting(xform.MapID, bounds)) { if (grid.GridIndex == other.Index || !bodyQuery.TryGetComponent(other.GridEntityId, out var body) || body.Mass < ShuttleCallMassThreshold) { continue; } reason = Loc.GetString("cargo-shuttle-console-proximity"); return(false); } return(true); }
private List <GasOverlayChunk> GetChunksInRange(IEntity entity) { var inRange = new List <GasOverlayChunk>(); // This is the max in any direction that we can get a chunk (e.g. max 2 chunks away of data). var(maxXDiff, maxYDiff) = ((int)(_updateRange / ChunkSize) + 1, (int)(_updateRange / ChunkSize) + 1); var worldBounds = Box2.CenteredAround(entity.Transform.WorldPosition, new Vector2(_updateRange, _updateRange)); foreach (var grid in _mapManager.FindGridsIntersecting(entity.Transform.MapID, worldBounds)) { if (!_overlay.TryGetValue(grid.Index, out var chunks)) { continue; } var entityTile = grid.GetTileRef(entity.Transform.Coordinates).GridIndices; for (var x = -maxXDiff; x <= maxXDiff; x++) { for (var y = -maxYDiff; y <= maxYDiff; y++) { var chunkIndices = GetGasChunkIndices(new MapIndices(entityTile.X + x * ChunkSize, entityTile.Y + y * ChunkSize)); if (!chunks.TryGetValue(chunkIndices, out var chunk)) { continue; } // Now we'll check if it's in range and relevant for us // (e.g. if we're on the very edge of a chunk we may need more chunks). var(xDiff, yDiff) = (chunkIndices.X - entityTile.X, chunkIndices.Y - entityTile.Y); if (xDiff > 0 && xDiff > _updateRange || yDiff > 0 && yDiff > _updateRange || xDiff < 0 && Math.Abs(xDiff + ChunkSize) > _updateRange || yDiff < 0 && Math.Abs(yDiff + ChunkSize) > _updateRange) { continue; } inRange.Add(chunk); } } } return(inRange); }
private void DestroyTiles(SingularityComponent component) { if (!component.Owner.TryGetComponent(out PhysicsComponent? physicsComponent)) { return; } var worldBox = physicsComponent.GetWorldAABB(); foreach (var grid in _mapManager.FindGridsIntersecting(component.Owner.Transform.MapID, worldBox)) { foreach (var tile in grid.GetTilesIntersecting(worldBox)) { grid.SetTile(tile.GridIndices, Tile.Empty); } } }
/// <summary> /// Destroy any grid tiles within the relevant Level range. /// </summary> private void DestroyTiles(ServerSingularityComponent component, TransformComponent xform, Vector2 worldPos) { var radius = DestroyTileRange(component); var circle = new Circle(worldPos, radius); var box = new Box2(worldPos - radius, worldPos + radius); foreach (var grid in _mapManager.FindGridsIntersecting(xform.MapID, box)) { // Bundle these together so we can use the faster helper to set tiles. var toDestroy = new List <(Vector2i, Tile)>(); foreach (var tile in grid.GetTilesIntersecting(circle)) { if (tile.Tile.IsEmpty) { continue; } // Avoid ripping up tiles that may be essential to containment if (component.Level < 5) { var canDelete = true; foreach (var ent in grid.GetAnchoredEntities(tile.GridIndices)) { if (EntityManager.HasComponent <ContainmentFieldComponent>(ent) || EntityManager.HasComponent <ContainmentFieldGeneratorComponent>(ent)) { canDelete = false; break; } } if (!canDelete) { continue; } } toDestroy.Add((tile.GridIndices, Tile.Empty)); } component.Energy += TileEnergyGain * toDestroy.Count; grid.SetTiles(toDestroy); } }
/// <summary> /// Destroy any grid tiles within the relevant Level range. /// </summary> private void DestroyTiles(ServerSingularityComponent component, Vector2 worldPos) { var radius = DestroyTileRange(component); var circle = new Circle(worldPos, radius); var box = new Box2(worldPos - radius, worldPos + radius); foreach (var grid in _mapManager.FindGridsIntersecting(EntityManager.GetComponent <TransformComponent>(component.Owner).MapID, box)) { foreach (var tile in grid.GetTilesIntersecting(circle)) { if (tile.Tile.IsEmpty) { continue; } grid.SetTile(tile.GridIndices, Tile.Empty); component.Energy += TileEnergyGain; } } }
private Dictionary <EntityUid, HashSet <Vector2i> > GetChunksForViewers( HashSet <EntityUid> viewers, int chunkSize, ObjectPool <HashSet <Vector2i> > indexPool, ObjectPool <Dictionary <EntityUid, HashSet <Vector2i> > > viewerPool, float viewEnlargement, EntityQuery <TransformComponent> xformQuery) { Dictionary <EntityUid, HashSet <Vector2i> > chunks = viewerPool.Get(); DebugTools.Assert(chunks.Count == 0); foreach (var viewerUid in viewers) { var xform = xformQuery.GetComponent(viewerUid); var pos = _transform.GetWorldPosition(xform, xformQuery); var bounds = _baseViewBounds.Translated(pos).Enlarged(viewEnlargement); foreach (var grid in _mapManager.FindGridsIntersecting(xform.MapID, bounds, true)) { if (!chunks.TryGetValue(grid.GridEntityId, out var set)) { chunks[grid.GridEntityId] = set = indexPool.Get(); DebugTools.Assert(set.Count == 0); } var enumerator = new ChunkIndicesEnumerator(_transform.GetInvWorldMatrix(grid.GridEntityId, xformQuery).TransformBox(bounds), chunkSize); while (enumerator.MoveNext(out var indices)) { set.Add(indices.Value); } } } return(chunks); }
protected override void Draw(DrawingHandleBase handle, OverlaySpace overlay) { var drawHandle = (DrawingHandleWorld)handle; var mapId = _eyeManager.CurrentMap; var eye = _eyeManager.CurrentEye; var worldBounds = Box2.CenteredAround(eye.Position.Position, _clyde.ScreenSize / (float)EyeManager.PixelsPerMeter * eye.Zoom); // IF YOU ARE ABOUT TO INTRODUCE CHUNKING OR SOME OTHER OPTIMIZATION INTO THIS CODE: // -- THINK! -- // 1. "Is this going to make a critical atmos debugging tool harder to debug itself?" // 2. "Is this going to do anything that could cause the atmos debugging tool to use resources, server-side or client-side, when nobody's using it?" // 3. "Is this going to make it harder for atmos programmers to add data that may not be chunk-friendly into the atmos debugger?" // Nanotrasen needs YOU! to avoid premature optimization in critical debugging tools - 20kdc foreach (var mapGrid in _mapManager.FindGridsIntersecting(mapId, worldBounds)) { if (!_atmosDebugOverlaySystem.HasData(mapGrid.Index)) { continue; } var gridBounds = new Box2(mapGrid.WorldToLocal(worldBounds.BottomLeft), mapGrid.WorldToLocal(worldBounds.TopRight)); for (var pass = 0; pass < 2; pass++) { foreach (var tile in mapGrid.GetTilesIntersecting(gridBounds)) { var dataMaybeNull = _atmosDebugOverlaySystem.GetData(mapGrid.Index, tile.GridIndices); if (dataMaybeNull != null) { var data = (SharedAtmosDebugOverlaySystem.AtmosDebugOverlayData)dataMaybeNull !; if (pass == 0) { // -- Mole Count -- float total = 0; switch (_atmosDebugOverlaySystem.CfgMode) { case AtmosDebugOverlayMode.TotalMoles: foreach (float f in data.Moles) { total += f; } break; case AtmosDebugOverlayMode.GasMoles: total = data.Moles[_atmosDebugOverlaySystem.CfgSpecificGas]; break; case AtmosDebugOverlayMode.Temperature: total = data.Temperature; break; } var interp = ((total - _atmosDebugOverlaySystem.CfgBase) / _atmosDebugOverlaySystem.CfgScale); Color res; if (_atmosDebugOverlaySystem.CfgCBM) { // Greyscale interpolation res = Color.InterpolateBetween(Color.Black, Color.White, interp); } else { // Red-Green-Blue interpolation if (interp < 0.5f) { res = Color.InterpolateBetween(Color.Red, Color.Green, interp * 2); } else { res = Color.InterpolateBetween(Color.Green, Color.Blue, (interp - 0.5f) * 2); } } res = res.WithAlpha(0.75f); drawHandle.DrawRect(Box2.FromDimensions(mapGrid.LocalToWorld(new Vector2(tile.X, tile.Y)), new Vector2(1, 1)), res); } else if (pass == 1) { // -- Blocked Directions -- void CheckAndShowBlockDir(AtmosDirection dir) { if (data.BlockDirection.HasFlag(dir)) { // Account for South being 0. var atmosAngle = dir.ToAngle() - Angle.FromDegrees(90); var atmosAngleOfs = atmosAngle.ToVec() * 0.45f; var atmosAngleOfsR90 = new Vector2(atmosAngleOfs.Y, -atmosAngleOfs.X); var tileCentre = new Vector2(tile.X + 0.5f, tile.Y + 0.5f); var basisA = mapGrid.LocalToWorld(tileCentre + atmosAngleOfs - atmosAngleOfsR90); var basisB = mapGrid.LocalToWorld(tileCentre + atmosAngleOfs + atmosAngleOfsR90); drawHandle.DrawLine(basisA, basisB, Color.Azure); } } CheckAndShowBlockDir(AtmosDirection.North); CheckAndShowBlockDir(AtmosDirection.South); CheckAndShowBlockDir(AtmosDirection.East); CheckAndShowBlockDir(AtmosDirection.West); // -- Pressure Direction -- if (data.PressureDirection != AtmosDirection.Invalid) { // Account for South being 0. var atmosAngle = data.PressureDirection.ToAngle() - Angle.FromDegrees(90); var atmosAngleOfs = atmosAngle.ToVec() * 0.4f; var tileCentre = new Vector2(tile.X + 0.5f, tile.Y + 0.5f); var basisA = mapGrid.LocalToWorld(tileCentre); var basisB = mapGrid.LocalToWorld(tileCentre + atmosAngleOfs); drawHandle.DrawLine(basisA, basisB, Color.Blue); } // -- Excited Groups -- if (data.InExcitedGroup) { var tilePos = new Vector2(tile.X, tile.Y); var basisA = mapGrid.LocalToWorld(tilePos); var basisB = mapGrid.LocalToWorld(tilePos + new Vector2(1.0f, 1.0f)); var basisC = mapGrid.LocalToWorld(tilePos + new Vector2(0.0f, 1.0f)); var basisD = mapGrid.LocalToWorld(tilePos + new Vector2(1.0f, 0.0f)); drawHandle.DrawLine(basisA, basisB, Color.Cyan); drawHandle.DrawLine(basisC, basisD, Color.Cyan); } } } } } } }
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)); } }
protected override void Draw(DrawingHandleBase handle, OverlaySpace overlay) { var drawHandle = (DrawingHandleWorld)handle; var mapId = _eyeManager.CurrentMap; var eye = _eyeManager.CurrentEye; var worldBounds = Box2.CenteredAround(eye.Position.Position, _clyde.ScreenSize / (float)EyeManager.PixelsPerMeter * eye.Zoom); // IF YOU ARE ABOUT TO INTRODUCE CHUNKING OR SOME OTHER OPTIMIZATION INTO THIS CODE: // -- THINK! -- // 1. "Is this going to make a critical atmos debugging tool harder to debug itself?" // 2. "Is this going to do anything that could cause the atmos debugging tool to use resources, server-side or client-side, when nobody's using it?" // 3. "Is this going to make it harder for atmos programmers to add data that may not be chunk-friendly into the atmos debugger?" // Nanotrasen needs YOU! to avoid premature optimization in critical debugging tools - 20kdc foreach (var mapGrid in _mapManager.FindGridsIntersecting(mapId, worldBounds)) { if (!_atmosDebugOverlaySystem.HasData(mapGrid.Index)) { continue; } var gridBounds = new Box2(mapGrid.WorldToLocal(worldBounds.BottomLeft), mapGrid.WorldToLocal(worldBounds.TopRight)); for (var pass = 0; pass < 3; pass++) { foreach (var tile in mapGrid.GetTilesIntersecting(gridBounds)) { var dataMaybeNull = _atmosDebugOverlaySystem.GetData(mapGrid.Index, tile.GridIndices); if (dataMaybeNull != null) { var data = (SharedAtmosDebugOverlaySystem.AtmosDebugOverlayData)dataMaybeNull !; if (pass == 0) { float total = 0; foreach (float f in data.Moles) { total += f; } var interp = total / (Atmospherics.MolesCellStandard * 2); var res = Color.InterpolateBetween(Color.Red, Color.Green, interp).WithAlpha(0.75f); drawHandle.DrawRect(Box2.FromDimensions(mapGrid.LocalToWorld(new Vector2(tile.X, tile.Y)), new Vector2(1, 1)), res); } else if (pass == 1) { if (data.PressureDirection != AtmosDirection.Invalid) { var atmosAngle = data.PressureDirection.ToAngle(); var atmosAngleOfs = atmosAngle.ToVec() * 0.4f; var tileCentre = new Vector2(tile.X + 0.5f, tile.Y + 0.5f); var basisA = mapGrid.LocalToWorld(tileCentre); var basisB = mapGrid.LocalToWorld(tileCentre + atmosAngleOfs); drawHandle.DrawLine(basisA, basisB, Color.Blue); } } else if (pass == 2) { if (data.InExcitedGroup) { var tilePos = new Vector2(tile.X, tile.Y); var basisA = mapGrid.LocalToWorld(tilePos); var basisB = mapGrid.LocalToWorld(tilePos + new Vector2(1.0f, 1.0f)); var basisC = mapGrid.LocalToWorld(tilePos + new Vector2(0.0f, 1.0f)); var basisD = mapGrid.LocalToWorld(tilePos + new Vector2(1.0f, 0.0f)); drawHandle.DrawLine(basisA, basisB, Color.Cyan); drawHandle.DrawLine(basisC, basisD, Color.Cyan); } } } } } } }