/// <summary> /// Generates sidewalk border inside the target rectangle. /// </summary> /// <param name="target">Target side rectangle</param> /// <param name="sidewalkTexture">Used texture</param> /// <returns>Rest of the target rectangle what's empty</returns> private Rectangle GenerateSidewalks(Rectangle target, Texture2D sidewalkTexture) { for (int x = 0; x < target.Width; x++) { foreach (int y in new int[] { 0, target.Height - 1 }) { int X = target.X + x; int Y = target.Y + y; mapBitmap[X * bitmapSize.Height + Y] = MapFillType.Sidewalk; FlatObject sidewalk = new FlatObject(new PositionInTown(this, new Vector2(X * SquareWidth, Y * SquareWidth)), 0, new Vector2(SquareWidth, SquareWidth), sidewalkTexture); groundObjects.AddLast(sidewalk); } } for (int y = 1; y < (target.Height - 1); y++) { foreach (int x in new int[] { 0, target.Width - 1 }) { int X = target.X + x; int Y = target.Y + y; mapBitmap[X * bitmapSize.Height + Y] = MapFillType.Sidewalk; FlatObject sidewalk = new FlatObject(new PositionInTown(this, new Vector2(X * SquareWidth, Y * SquareWidth)), 0, new Vector2(SquareWidth, SquareWidth), sidewalkTexture); groundObjects.AddLast(sidewalk); } } return new Rectangle(target.X + 1, target.Y + 1, target.Width - 2, target.Height - 2); }
/// <summary> /// Generates sidewalks what surround quarter border road and weren't generated by interfaces. /// </summary> /// <param name="emptyRanges">Ranges for sidewalks placement</param> /// <param name="sidewalkModel">Used sidewalk texture</param> private void GenerateRestOfBorderSidewalks(Dictionary<TownQuarterInterfacePosition, List<Range>> emptyRanges, Texture2D sidewalkTexture) { // Corners foreach (Tuple<int, int> p in new Tuple<int, int>[] { new Tuple<int, int>(BlockWidth - 1, BlockWidth), new Tuple<int, int>(BlockWidth - 1, BlockWidth - 1), new Tuple<int, int>(BlockWidth, BlockWidth - 1), new Tuple<int, int>(bitmapSize.Width - BlockWidth - 1, BlockWidth - 1), new Tuple<int, int>(bitmapSize.Width - BlockWidth, BlockWidth - 1), new Tuple<int, int>(bitmapSize.Width - BlockWidth, BlockWidth), new Tuple<int, int>(bitmapSize.Width - BlockWidth, bitmapSize.Height - BlockWidth - 1), new Tuple<int, int>(bitmapSize.Width - BlockWidth, bitmapSize.Height - BlockWidth), new Tuple<int, int>(bitmapSize.Width - BlockWidth - 1, bitmapSize.Height - BlockWidth), new Tuple<int, int>(BlockWidth, bitmapSize.Height - BlockWidth), new Tuple<int, int>(BlockWidth-1, bitmapSize.Height - BlockWidth), new Tuple<int, int>(BlockWidth-1, bitmapSize.Height - BlockWidth - 1) }) { int x = p.Item1, y = p.Item2; mapBitmap[x * bitmapSize.Height + y] = MapFillType.Sidewalk; FlatObject sidewalk = new FlatObject(new PositionInTown(this, new Vector2(x * SquareWidth, y * SquareWidth)), 0, new Vector2(SquareWidth, SquareWidth), sidewalkTexture); groundObjects.AddLast(sidewalk); } // Ranges foreach (KeyValuePair<TownQuarterInterfacePosition, List<Range>> sideRanges in emptyRanges) { foreach (Range range in sideRanges.Value) { for (int i = 0; i < range.Length; i++) { int x, y; switch (sideRanges.Key) { case TownQuarterInterfacePosition.Top: x = range.Begin + i; y = BlockWidth - 1; break; case TownQuarterInterfacePosition.Right: x = bitmapSize.Width - BlockWidth; y = range.Begin + i; break; case TownQuarterInterfacePosition.Bottom: x = range.Begin + i; y = bitmapSize.Height - BlockWidth; break; case TownQuarterInterfacePosition.Left: x = BlockWidth - 1; y = range.Begin + i; break; default: throw new InvalidOperationException("Unknown TownQuarterInterfacePosition value."); } mapBitmap[x * bitmapSize.Height + y] = MapFillType.Sidewalk; FlatObject sidewalk = new FlatObject(new PositionInTown(this, new Vector2(x * SquareWidth, y * SquareWidth)), 0, new Vector2(SquareWidth, SquareWidth), sidewalkTexture); groundObjects.AddLast(sidewalk); } } } }
/// <summary> /// Generates road and sidewalks interfaces - street for joining with other quarters. /// </summary> /// <param name="degree">Number of'quarters neighbors - degree of vertex</param> /// <param name="roadTexture">Used road texture</param> /// <param name="sidewalkTexture">Used sidewalk texture</param> private void GenerateInterfaces(int degree, Texture2D roadTexture, Texture2D sidewalkTexture) { Dictionary<TownQuarterInterfacePosition, List<Range>> emptyRanges = new Dictionary<TownQuarterInterfacePosition, List<Range>>(4); emptyRanges.Add(TownQuarterInterfacePosition.Top, new List<Range>(new Range[] { new Range(BlockWidth + 1, bitmapSize.Width - BlockWidth - 1) })); emptyRanges.Add(TownQuarterInterfacePosition.Bottom, new List<Range>(new Range[] { new Range(BlockWidth + 1, bitmapSize.Width - BlockWidth - 1) })); emptyRanges.Add(TownQuarterInterfacePosition.Left, new List<Range>(new Range[] { new Range(BlockWidth + 1, bitmapSize.Height - BlockWidth - 1) })); emptyRanges.Add(TownQuarterInterfacePosition.Right, new List<Range>(new Range[] { new Range(BlockWidth + 1, bitmapSize.Height - BlockWidth - 1) })); for (int i = 0; i < degree; i++) { TownQuarterInterfacePosition side; List<TownQuarterInterfacePosition> possibleSides = new List<TownQuarterInterfacePosition>( from key in emptyRanges.Keys where emptyRanges[key].Count(rangeItem => rangeItem.Length > 2 * BlockWidth + 1) > 0 select key ); if (possibleSides.Count < 1) { throw new NoSpaceForInterfaceException("This quarter has already full all sides of interfaces. Degree argument is too big."); } side = possibleSides[game.Random.Next(0, possibleSides.Count - 1)]; List<Range> possibleRanges = emptyRanges[side].FindAll(rangeItem => rangeItem.Length > 2 * BlockWidth + 1); int rangeIndex = game.Random.Next(0, possibleRanges.Count - 1); Range range = possibleRanges[rangeIndex]; emptyRanges[side].Remove(range); int position = range.Begin + (int)((0.35 + game.Random.NextDouble() * 0.3) // percentage position in range * range.Length); emptyRanges[side].Add(new Range(range.Begin, position - 1)); emptyRanges[side].Add(new Range(position + 1, range.End)); AxisDirection direction = (side == TownQuarterInterfacePosition.Right || side == TownQuarterInterfacePosition.Left) ? AxisDirection.Horizontal : AxisDirection.Vertical; TownQuarterInterface iface = new TownQuarterInterface { SidePosition = side, Quarter = this, OppositeInterface = null }; for (int p = 0; p < BlockWidth; p++) { int rx, ry, slx, sly, srx, sry; switch (direction) { case AxisDirection.Horizontal: rx = (p + (side == TownQuarterInterfacePosition.Left ? 0 : bitmapSize.Width - BlockWidth)); ry = position; slx = rx; srx = rx; sly = ry - 1; sry = ry + 1; break; case AxisDirection.Vertical: rx = position; ry = (p + (side == TownQuarterInterfacePosition.Top ? 0 : bitmapSize.Height - BlockWidth)); slx = rx - 1; srx = rx + 1; sly = ry; sry = ry; break; default: throw new InvalidOperationException("Unknown AxisDirection value."); } if (direction == AxisDirection.Horizontal) iface.BitmapPosition = ry; if (direction == AxisDirection.Vertical) iface.BitmapPosition = rx; mapBitmap[rx * bitmapSize.Height + ry] = MapFillType.Road; mapBitmap[slx * bitmapSize.Height + sly] = MapFillType.Sidewalk; mapBitmap[srx * bitmapSize.Height + sry] = MapFillType.Sidewalk; FlatObject road = new FlatObject(new PositionInTown(this, new Vector2(rx * SquareWidth, ry * SquareWidth)), 0, new Vector2(SquareWidth, SquareWidth), roadTexture); FlatObject sidewalkL = new FlatObject(new PositionInTown(this, new Vector2(slx * SquareWidth, sly * SquareWidth)), 0, new Vector2(SquareWidth, SquareWidth), sidewalkTexture); FlatObject sidewalkR = new FlatObject(new PositionInTown(this, new Vector2(srx * SquareWidth, sry * SquareWidth)), 0, new Vector2(SquareWidth, SquareWidth), sidewalkTexture); groundObjects.AddLast(road); groundObjects.AddLast(sidewalkL); groundObjects.AddLast(sidewalkR); } //wall generator { Texture2D wallTexture = game.ContentRepository.InterfaceWall; float wallWidth = 6f; //m const float wallHeight = 4.5f; int count = (int)((BlockWidth - 1) * SquareWidth / wallWidth); //minus sidewalk wallWidth = (BlockWidth - 1) * SquareWidth / count; const float ifaceWallEpsilon = 0.006f; // 3mm for (int p = 0; p < count; p++) { Vector2 beginL, endL, beginR, endR; switch (direction) { case AxisDirection.Horizontal: beginL.X = p * wallWidth + (side == TownQuarterInterfacePosition.Left ? 0 : bitmapSize.Width - BlockWidth + 1) * SquareWidth; beginL.Y = (position - 1) * SquareWidth + ifaceWallEpsilon; endL = beginL; endL.X += wallWidth; beginR.X = p * wallWidth + (side == TownQuarterInterfacePosition.Left ? 0 : bitmapSize.Width - BlockWidth + 1) * SquareWidth; beginR.Y = (position + 2) * SquareWidth - ifaceWallEpsilon; endR = beginR; endR.X += wallWidth; break; case AxisDirection.Vertical: beginL.Y = p * wallWidth + (side == TownQuarterInterfacePosition.Top ? 0 : bitmapSize.Height - BlockWidth + 1) * SquareWidth; beginL.X = (position - 1) * SquareWidth + ifaceWallEpsilon; endL = beginL; endL.Y += wallWidth; beginR.Y = p * wallWidth + (side == TownQuarterInterfacePosition.Top ? 0 : bitmapSize.Height - BlockWidth + 1) * SquareWidth; beginR.X = (position + 2) * SquareWidth - ifaceWallEpsilon; endR = beginR; endR.Y += wallWidth; break; default: throw new InvalidOperationException("Unknown AxisDirection value."); } Plate wallL = new Plate(this, beginL.ToVector3(wallHeight), endL.ToVector3(wallHeight), beginL.ToVector3(0), endL.ToVector3(0), wallTexture, wallTexture, true); solidPlates.AddLast(wallL); Plate wallr = new Plate(this, beginR.ToVector3(wallHeight), endR.ToVector3(wallHeight), beginR.ToVector3(0), endR.ToVector3(0), wallTexture, wallTexture, true); solidPlates.AddLast(wallr); } } interfaces.Add(iface); } GenerateRestOfBorderSidewalks(emptyRanges, sidewalkTexture); GenerateBorderBuildings(emptyRanges); }
/// <summary> /// Generates ground grass. /// </summary> /// <param name="grassTexture">Grass texture</param> /// <param name="emptyRectaglesInsideSidewalks">The target rectangles for the grass filling</param> private void GenerateGrass(Texture2D grassTexture, List<Rectangle> emptyRectaglesInsideSidewalks) { float grassWidth = 13.5f; //m foreach (Rectangle emptyRect in emptyRectaglesInsideSidewalks) { int xCount = (int)((emptyRect.Width * SquareWidth) / grassWidth) + 1; int yCount = (int)((emptyRect.Height * SquareWidth) / grassWidth) + 1; Vector2 size = new Vector2((emptyRect.Width * SquareWidth) / xCount, (emptyRect.Height * SquareWidth) / yCount); for (int x = 0; x < xCount; x++) { for (int y = 0; y < yCount; y++) { Vector2 position = new Vector2(emptyRect.X * SquareWidth + x * size.X, emptyRect.Y * SquareWidth + y * size.Y); FlatObject grass = new FlatObject(new PositionInTown(this, position), 0, size, grassTexture); groundObjects.AddFirst(grass); } } } }
/// <summary> /// Generates border road network. /// </summary> /// <param name="roadTexture">Road texture</param> /// <returns>Empty rectangle inside the bitmap</returns> private Rectangle GenerateBorderRoads(Texture2D roadTexture) { int xOffset = BlockWidth; int yOffset = BlockWidth; int xCount = bitmapSize.Width - 2 * xOffset; int yCount = bitmapSize.Height - 2 * yOffset; for (int x = 0; x < xCount; x++) { int X = x + xOffset; foreach (int y in new int[] { 0, yCount - 1 }) { int Y = y + yOffset; mapBitmap[X * bitmapSize.Height + Y] = MapFillType.Road; FlatObject road = new FlatObject(new PositionInTown(this, new Vector2(X * SquareWidth, Y * SquareWidth)), 0, new Vector2(SquareWidth, SquareWidth), roadTexture); groundObjects.AddLast(road); } } for (int y = 1; y < yCount - 1; y++) { int Y = y + yOffset; foreach (int x in new int[] { 0, xCount - 1 }) { int X = x + xOffset; mapBitmap[X * bitmapSize.Height + Y] = MapFillType.Road; FlatObject road = new FlatObject(new PositionInTown(this, new Vector2(X * SquareWidth, Y * SquareWidth)), 0, new Vector2(SquareWidth, SquareWidth), roadTexture); groundObjects.AddLast(road); } } return new Rectangle(xOffset + 1, yOffset + 1, xCount - 2, yCount - 2); }
/// <summary> /// Splits the given rectangle with a road. /// </summary> /// <param name="target">Splited rectangle</param> /// <param name="roadTexture">Road texture</param> /// <returns>Array of new empty rectangles inside the splited rectangle</returns> private Rectangle[] AddSplittingRoad(ref Rectangle target, Texture2D roadTexture) { AxisDirection direction; if (target.Width < target.Height) direction = AxisDirection.Horizontal; else direction = AxisDirection.Vertical; int size = (direction == AxisDirection.Horizontal ? target.Height : target.Width); int secondDimensionSize = (direction == AxisDirection.Horizontal ? target.Width : target.Height); Random rand = new Random(); int splitPosition = (int)( (0.35 + rand.NextDouble() * 0.3) //percentage side * (size - 1)); for (int i = 0; i < secondDimensionSize; i++) { Vector2 position = Vector2.Zero; int bitmapIndex = -1; switch (direction) { case AxisDirection.Horizontal: position = new Vector2((i + target.X) * SquareWidth, (splitPosition + target.Y) * SquareWidth); bitmapIndex = (i + target.X) * bitmapSize.Height + (splitPosition + target.Y); break; case AxisDirection.Vertical: position = new Vector2((splitPosition + target.X) * SquareWidth, (i + target.Y) * SquareWidth); bitmapIndex = (splitPosition + target.X) * bitmapSize.Height + (i + target.Y); break; default: throw new InvalidOperationException("Unknown AxisDirection for splitting."); } mapBitmap[bitmapIndex] = MapFillType.Road; FlatObject road = new FlatObject(new PositionInTown(this, position), 0, new Vector2(SquareWidth, SquareWidth), roadTexture); groundObjects.AddLast(road); } Rectangle[] emptyRectangles = new Rectangle[2]; if (direction == AxisDirection.Horizontal) { emptyRectangles[0] = new Rectangle(target.X, target.Y, target.Width, splitPosition); emptyRectangles[1] = new Rectangle(target.X, target.Y + splitPosition + 1, target.Width, target.Height - splitPosition - 1); } else if (direction == AxisDirection.Vertical) { emptyRectangles[0] = new Rectangle(target.X, target.Y, splitPosition, target.Height); emptyRectangles[1] = new Rectangle(target.X + splitPosition + 1, target.Y, target.Width - splitPosition - 1, target.Height); } return emptyRectangles; }