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);
                }
            }
        }
Beispiel #3
0
        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);
    }
Beispiel #5
0
        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);
        }
Beispiel #6
0
        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);
                }
            }
        }
Beispiel #7
0
        /// <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);
            }
        }
Beispiel #8
0
        /// <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);
    }
Beispiel #10
0
        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);
                                }
                            }
                        }
                    }
                }
            }
        }
Beispiel #11
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));
            }
        }
Beispiel #12
0
        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);
                                }
                            }
                        }
                    }
                }
            }
        }