#region Copyright & License Information /* * Copyright 2007-2010 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, * see LICENSE. */ #endregion using System; using System.Drawing; using System.Drawing.Imaging; using System.Linq; using OpenRA.FileFormats; using OpenRA.Traits; namespace OpenRA.Graphics { class Minimap { readonly World world; Sheet sheet; SpriteRenderer rgbaRenderer; Sprite sprite; Bitmap terrain, customLayer; Rectangle bounds; const int alpha = 230; public Minimap(World world, Renderer r) { this.world = world; sheet = new Sheet(r, new Size(world.Map.MapSize.X, world.Map.MapSize.Y)); rgbaRenderer = r.RgbaSpriteRenderer; var size = Math.Max(world.Map.Width, world.Map.Height); var dw = (size - world.Map.Width) / 2; var dh = (size - world.Map.Height) / 2; bounds = new Rectangle(world.Map.TopLeft.X - dw, world.Map.TopLeft.Y - dh, size, size); sprite = new Sprite(sheet, bounds, TextureChannel.Alpha); shroudColor = Color.FromArgb(alpha, Color.Black); } public static Rectangle MakeMinimapBounds(Map m) { var size = Math.Max(m.Width, m.Height); var dw = (size - m.Width) / 2; var dh = (size - m.Height) / 2; return new Rectangle(m.TopLeft.X - dw, m.TopLeft.Y - dh, size, size); } static Color shroudColor; public void InvalidateCustom() { customLayer = null; } public static Bitmap RenderTerrainBitmap(Map map, TileSet tileset) { var terrain = new Bitmap(map.MapSize.X, map.MapSize.Y); var bitmapData = terrain.LockBits(new Rectangle(0, 0, terrain.Width, terrain.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); unsafe { int* c = (int*)bitmapData.Scan0; for (var x = 0; x < map.MapSize.X; x++) for (var y = 0; y < map.MapSize.Y; y++) { var type = tileset.GetTerrainType(map.MapTiles[x, y]); *(c + (y * bitmapData.Stride >> 2) + x) = map.IsInMap(x, y) ? Color.FromArgb(alpha, tileset.Terrain[type].Color).ToArgb() : shroudColor.ToArgb(); } } terrain.UnlockBits(bitmapData); return terrain; } public void Update() { if (terrain == null) terrain = RenderTerrainBitmap(world.Map, world.TileSet); // Custom terrain layer if (customLayer == null) { customLayer = new Bitmap(terrain); for (var x = world.Map.TopLeft.X; x < world.Map.BottomRight.X; x++) for (var y = world.Map.TopLeft.Y; y < world.Map.BottomRight.Y; y++) { var customTerrain = world.WorldActor.traits.WithInterface() .Select( t => t.GetTerrainType(new int2(x,y)) ) .FirstOrDefault( t => t != null ); if (customTerrain == null) continue; customLayer.SetPixel(x, y, Color.FromArgb(alpha, world.TileSet.Terrain[customTerrain].Color)); } } if (!world.GameHasStarted || !world.Queries.OwnedBy[world.LocalPlayer].WithTrait().Any()) return; var bitmap = new Bitmap(customLayer); var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); unsafe { int* c = (int*)bitmapData.Scan0; foreach (var a in world.Queries.WithTrait().Where(a => a.Actor.Owner != null && a.Actor.IsVisible())) *(c + (a.Actor.Location.Y * bitmapData.Stride >> 2) + a.Actor.Location.X) = Color.FromArgb(alpha, a.Actor.Owner.Color).ToArgb(); for (var x = world.Map.TopLeft.X; x < world.Map.BottomRight.X; x++) for (var y = world.Map.TopLeft.Y; y < world.Map.BottomRight.Y; y++) { if (!world.LocalPlayer.Shroud.DisplayOnRadar(x, y)) { *(c + (y * bitmapData.Stride >> 2) + x) = shroudColor.ToArgb(); continue; } var b = world.WorldActor.traits.Get().GetBuildingAt(new int2(x, y)); if (b != null) *(c + (y * bitmapData.Stride >> 2) + x) = Color.FromArgb(alpha, b.Owner.Color).ToArgb(); } } bitmap.UnlockBits(bitmapData); sheet.Texture.SetData(bitmap); } public void Draw(RectangleF rect) { rgbaRenderer.DrawSprite(sprite, new float2(rect.X, rect.Y), "chrome", new float2(rect.Width, rect.Height)); rgbaRenderer.Flush(); } int2 CellToMinimapPixel(RectangleF viewRect, int2 p) { var fx = (float)(p.X - bounds.X) / bounds.Width; var fy = (float)(p.Y - bounds.Y) / bounds.Height; return new int2( (int)(viewRect.Width * fx + viewRect.Left), (int)(viewRect.Height * fy + viewRect.Top)); } public int2 MinimapPixelToCell(RectangleF viewRect, int2 p) { var fx = (float)(p.X - viewRect.Left) / viewRect.Width; var fy = (float)(p.Y - viewRect.Top) / viewRect.Height; return new int2( (int)(bounds.Width * fx + bounds.Left), (int)(bounds.Height * fy + bounds.Top)); } static int NextPowerOf2(int v) { --v; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; ++v; return v; } public static Bitmap RenderMapPreview(MapStub stub) { Map map = stub.Map; var tileset = Rules.TileSets[map.Tileset]; var size = NextPowerOf2(Math.Max(map.Width, map.Height)); Bitmap terrain = new Bitmap(size, size); var bitmapData = terrain.LockBits(new Rectangle(0, 0, terrain.Width, terrain.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); unsafe { int* c = (int*)bitmapData.Scan0; for (var x = 0; x < map.Width; x++) for (var y = 0; y < map.Height; y++) { var mapX = x + map.TopLeft.X; var mapY = y + map.TopLeft.Y; var type = tileset.GetTerrainType(map.MapTiles[mapX, mapY]); string res = null; if (map.MapResources[mapX, mapY].type != 0) res = Rules.Info["world"].Traits.WithInterface() .Where(t => t.ResourceType == map.MapResources[mapX, mapY].type) .Select(t => t.TerrainType).FirstOrDefault(); if (res != null) type = res; *(c + (y * bitmapData.Stride >> 2) + x) = tileset.Terrain[type].Color.ToArgb(); } } terrain.UnlockBits(bitmapData); return terrain; } } }