Exemplo n.º 1
0
        public ProjectedCellRegion(Map map, PPos topLeft, PPos bottomRight)
        {
            TopLeft     = topLeft;
            BottomRight = bottomRight;

            // The projection from MPos -> PPos cannot produce a larger V coordinate
            // so the top edge of the MPos region is the same as the PPos region.
            // (in fact the cells are identical if height == 0)
            mapTopLeft = (MPos)topLeft;

            // The bottom edge is trickier: cells at MPos.V > bottomRight.V may have
            // been projected into this region if they have height > 0.
            // Each height step is equivalent to 512 WRange units, which is one MPos
            // step for isometric cells, but only half a MPos step for classic cells. Doh!
            var maxHeight    = map.Grid.MaximumTerrainHeight;
            var heightOffset = map.Grid.Type == MapGridType.RectangularIsometric ? maxHeight : maxHeight / 2;

            // Use the MapHeight data array to clamp the bottom coordinate so it doesn't overflow the map
            mapBottomRight = map.MapHeight.Value.Clamp(new MPos(bottomRight.U, bottomRight.V + heightOffset));
        }
Exemplo n.º 2
0
        public PPos[] ProjectedCellsCovering(MPos uv)
        {
            // Shortcut for mods that don't use heightmaps
            if (MaximumTerrainHeight == 0)
            {
                return new[] { (PPos)uv }
            }
            ;

            if (!initializedCellProjection)
            {
                InitializeCellProjection();
            }

            if (!cellProjection.Contains(uv))
            {
                return(NoProjectedCells);
            }

            return(cellProjection[uv]);
        }
Exemplo n.º 3
0
        public void SetBounds(MPos tl, MPos br)
        {
            // The tl and br coordinates are inclusive, but the Rectangle
            // is exclusive.  Pad the right and bottom edges to match.
            Bounds            = Rectangle.FromLTRB(tl.U, tl.V, br.U + 1, br.V + 1);
            CellsInsideBounds = new CellRegion(TileShape, tl.ToCPos(this), br.ToCPos(this));

            // Directly calculate the projected map corners in world units avoiding unnecessary
            // conversions.  This abuses the definition that the width of the cell is always
            // 1024 units, and that the height of two rows is 2048 for classic cells and 1024
            // for diamond cells.
            var wtop    = tl.V * 1024;
            var wbottom = (br.V + 1) * 1024;

            if (TileShape == TileShape.Diamond)
            {
                wtop    /= 2;
                wbottom /= 2;
            }

            ProjectedTopLeft     = new WPos(tl.U * 1024, wtop, 0);
            ProjectedBottomRight = new WPos(br.U * 1024 - 1, wbottom - 1, 0);
        }
Exemplo n.º 4
0
        bool ContainsAllProjectedCellsCovering(MPos uv)
        {
            if (Grid.MaximumTerrainHeight == 0)
            {
                return(Contains((PPos)uv));
            }

            // If the cell has no valid projection, then we're off the map.
            var projectedCells = ProjectedCellsCovering(uv);

            if (projectedCells.Length == 0)
            {
                return(false);
            }

            foreach (var puv in projectedCells)
            {
                if (!Contains(puv))
                {
                    return(false);
                }
            }
            return(true);
        }
Exemplo n.º 5
0
 // Resolve an array index from map coordinates
 int Index(MPos uv)
 {
     return(uv.V * Size.Width + uv.U);
 }
Exemplo n.º 6
0
 public MPos Clamp(MPos uv)
 {
     return(uv.Clamp(new Rectangle(0, 0, Size.Width - 1, Size.Height - 1)));
 }
Exemplo n.º 7
0
 public bool Contains(MPos uv)
 {
     return(bounds.Contains(uv.U, uv.V));
 }
Exemplo n.º 8
0
        public byte[] SavePreview()
        {
            var tileset   = Rules.TileSet;
            var resources = Rules.Actors["world"].TraitInfos <ResourceTypeInfo>()
                            .ToDictionary(r => r.ResourceType, r => r.TerrainType);

            using (var stream = new MemoryStream())
            {
                var isRectangularIsometric = Grid.Type == MapGridType.RectangularIsometric;

                // Fudge the heightmap offset by adding as much extra as we need / can.
                // This tries to correct for our incorrect assumption that MPos == PPos
                var heightOffset = Math.Min(Grid.MaximumTerrainHeight, MapSize.Y - Bounds.Bottom);
                var width        = Bounds.Width;
                var height       = Bounds.Height + heightOffset;

                var bitmapWidth = width;
                if (isRectangularIsometric)
                {
                    bitmapWidth = 2 * bitmapWidth - 1;
                }

                using (var bitmap = new Bitmap(bitmapWidth, height))
                {
                    var bitmapData = bitmap.LockBits(bitmap.Bounds(),
                                                     ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

                    unsafe
                    {
                        var   colors = (int *)bitmapData.Scan0;
                        var   stride = bitmapData.Stride / 4;
                        Color leftColor, rightColor;

                        for (var y = 0; y < height; y++)
                        {
                            for (var x = 0; x < width; x++)
                            {
                                var uv           = new MPos(x + Bounds.Left, y + Bounds.Top);
                                var resourceType = Resources[uv].Type;
                                if (resourceType != 0)
                                {
                                    // Cell contains resources
                                    string res;
                                    if (!resources.TryGetValue(resourceType, out res))
                                    {
                                        continue;
                                    }

                                    leftColor = rightColor = tileset[tileset.GetTerrainIndex(res)].Color;
                                }
                                else
                                {
                                    // Cell contains terrain
                                    var type = tileset.GetTileInfo(Tiles[uv]);
                                    leftColor  = type != null ? type.LeftColor : Color.Black;
                                    rightColor = type != null ? type.RightColor : Color.Black;
                                }

                                if (isRectangularIsometric)
                                {
                                    // Odd rows are shifted right by 1px
                                    var dx = uv.V & 1;
                                    if (x + dx > 0)
                                    {
                                        colors[y * stride + 2 * x + dx - 1] = leftColor.ToArgb();
                                    }

                                    if (2 * x + dx < stride)
                                    {
                                        colors[y * stride + 2 * x + dx] = rightColor.ToArgb();
                                    }
                                }
                                else
                                {
                                    colors[y * stride + x] = leftColor.ToArgb();
                                }
                            }
                        }
                    }

                    bitmap.UnlockBits(bitmapData);
                    bitmap.Save(stream, ImageFormat.Png);
                }

                return(stream.ToArray());
            }
        }
Exemplo n.º 9
0
        public MPos Clamp(MPos uv)
        {
            if (Grid.MaximumTerrainHeight == 0)
            {
                return((MPos)Clamp((PPos)uv));
            }

            // Already in bounds, so don't need to do anything.
            if (ContainsAllProjectedCellsCovering(uv))
            {
                return(uv);
            }

            // Clamping map coordinates is trickier than it might first look!
            // This needs to handle three nasty cases:
            //  * The requested cell is well outside the map region
            //  * The requested cell is near the top edge inside the map but outside the projected layer
            //  * The clamped projected cell lands on a cliff face with no associated map cell
            //
            // Handling these cases properly requires abuse of our knowledge of the projection transform.
            //
            // The U coordinate doesn't change significantly in the projection, so clamp this
            // straight away and ensure the point is somewhere inside the map
            uv = cellProjection.Clamp(new MPos(uv.U.Clamp(Bounds.Left, Bounds.Right), uv.V));

            // Project this guessed cell and take the first available cell
            // If it is projected outside the layer, then make another guess.
            var allProjected = ProjectedCellsCovering(uv);
            var projected    = allProjected.Any() ? allProjected.First()
                                : new PPos(uv.U, uv.V.Clamp(Bounds.Top, Bounds.Bottom));

            // Clamp the projected cell to the map area
            projected = Clamp(projected);

            // Project the cell back into map coordinates.
            // This may fail if the projected cell covered a cliff or another feature
            // where there is a large change in terrain height.
            var unProjected = Unproject(projected);

            if (!unProjected.Any())
            {
                // Adjust V until we find a cell that works
                for (var x = 2; x <= 2 * Grid.MaximumTerrainHeight; x++)
                {
                    var dv   = ((x & 1) == 1 ? 1 : -1) * x / 2;
                    var test = new PPos(projected.U, projected.V + dv);
                    if (!Contains(test))
                    {
                        continue;
                    }

                    unProjected = Unproject(test);
                    if (unProjected.Any())
                    {
                        break;
                    }
                }

                // This shouldn't happen.  But if it does, return the original value and hope the caller doesn't explode.
                if (!unProjected.Any())
                {
                    Log.Write("debug", "Failed to clamp map cell {0} to map bounds", uv);
                    return(uv);
                }
            }

            return(projected.V == Bounds.Bottom ? unProjected.MaxBy(x => x.V) : unProjected.MinBy(x => x.V));
        }
Exemplo n.º 10
0
 public bool ShroudObscures(MPos uv)
 {
     return(RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(uv));
 }
Exemplo n.º 11
0
        public MPos Clamp(MPos uv)
        {
            var bounds = new Rectangle(Bounds.X, Bounds.Y, Bounds.Width - 1, Bounds.Height - 1);

            return(uv.Clamp(bounds));
        }