I was browsing the screenshot thread and I saw lots of bots with radars that show mob positions. However I noticed most of them did not provide a minimap overlay. Below is a quick guide on how to add minimap overlays to your radar. It should would for all versions of WoW, but I've only tested it on WoW Classic, and it only covers the Azeroth and Kalimdor continents.
Existing work and credit
Adding a minimap overlay is nothing new, neither is building a radar. There are many existing resources, however I could not find all the necessary information in one place today, and most of the existing code fragments on ownedcore are now 404. Hence this guide.
Most of the code below I wrote a long time ago, using this site as a reference. I cannot remember exactly what was original vs. not. I do know the following:
Getting minimap images
First we need to collect all of the minimap images for use in our program. The best way I know to do this is to obtain a copy of common.MPQ and common-2.MPQ from 3.3.5a WoW and extract them using WinMPQ.
- For the 3.3.5a files, look for an existing torrent. It should not be too hard to find since many private servers use 3.3.5a.
- For WinMPQ, it is freely available, just search for it.
The minimap images are stored in common.MPQ under `textures\Minimap\...` path. The filenames are hashes that are not too useful. In common-2.MPQ, there is a file `textures\Minimap\md5translate.trs` that translates between the hashes and the clean filenames, for example:
Code:
Azeroth\map39_26.blp 02dd4ce432e93438e7ea0ca0e41ff6b4.blp
Azeroth\map39_27.blp e455c8b91589193dd6a9def9271ae52c.blp
Azeroth\map39_28.blp 08fe4ea582021b905f37db3a881d6619.blp
...
Once you have extracted the .blp files for Azeroth and Kalimdor using WinMPQ, you should rename the files according to the mapping in md5translate.trs. You can write a Python script or similar for this.
Finally you will probably want to convert to PNG since it's easier to work with than blp. Try out the BLP2PNG utility, or search this site for other similar utilities.
Translating world coordinates to tiles
Each image is 256x256 in size. The translated filenames are of the format `mapII_JJ.blp` where II (tile_i below) and JJ (tile_j) correspond to world coordinates according to a formula:
Code:
const float TILE_SCALE_FACTOR = 533.5F;
int tile_i = 32 - (int)Math.Ceiling(player_y / TILE_SCALE_FACTOR);
int tile_j = 32 - (int)Math.Ceiling(player_x / TILE_SCALE_FACTOR);
For your convenience, here's a function that loads the correct image and draws it to a bitmap:
Code:
const int TILE_HEIGHT = 256;
private Bitmap AddContinentTile(Bitmap img, int current_continent, int i, int j, int mm_x, int mm_y)
{
Graphics G = Graphics.FromImage(img);
try
{
Image Minimap;
if (current_continent == 1)
{
Minimap = Image.FromFile("Maps\\Azeroth\\map" + i + "_" + j + ".png");
}
else if (current_continent == 0)
{
Minimap = Image.FromFile("Maps\\Kalimdor\\map" + i + "_" + j + ".png");
}
else
{
Minimap = null;
}
G.DrawImage(Minimap, mm_x, mm_y, TILE_HEIGHT, TILE_HEIGHT);
}
catch (Exception ex)
{
G.FillRectangle(new SolidBrush(Color.Black), new Rectangle(mm_x, mm_y, TILE_HEIGHT, TILE_HEIGHT));
}
G.Dispose();
return img;
}
Putting it together
So now it's just a matter of some careful calculations to position tiles appropriately, handle edges of tiles, and add zoom if desired.
First let's build an image of the current tile and the 8 surrounding tiles, forming a 3x3 grid with our current tile in the middle:
Code:
const int MM_WIDTH = 500; // width of your output
const int MM_HEIGHT = 500; // height of your output
private Bitmap GetNearbyMinimap(float local_x, float local_y, float zoom, int current_continent)
{
Bitmap Minimap = new Bitmap(TILE_HEIGHT * 3, TILE_HEIGHT * 3);
Graphics G = Graphics.FromImage(Minimap);
int tile_i = 32 - (int)Math.Ceiling(local_y / TILE_SCALE_FACTOR);
int tile_j = 32 - (int)Math.Ceiling(local_x / TILE_SCALE_FACTOR);
Minimap = AddContinentTile(Minimap, current_continent, tile_i, tile_j, (int)((MM_WIDTH) / 2) - (TILE_HEIGHT / 2), (int)((MM_HEIGHT) / 2) - (TILE_HEIGHT / 2));
// Add adjacent tiles
for (int i = tile_i - 1; i <= tile_i + 1; i++)
{
for (int j = tile_j - 1; j <= tile_j + 1; j++)
{
Minimap = AddContinentTile(Minimap, current_continent, i, j, TILE_HEIGHT + (TILE_HEIGHT * (i - tile_i)), TILE_HEIGHT + (TILE_HEIGHT * (j - tile_j)));
}
}
Bitmap ret = new Bitmap((int)(zoom * 3), (int)(zoom * 3));
Graphics G2 = Graphics.FromImage(ret);
G2.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
G2.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low;
G2.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
G2.DrawImage(Minimap, 0, 0, zoom * 3, zoom * 3);
return ret;
}
The final piece is to position the large combined minimap image so that our player is in the center:
Code:
private Bitmap DrawMinimapOverlay(Bitmap nearby_mm, Bitmap out, float local_x, float local_y, float zoom)
{
Graphics G = Graphics.FromImage(out);
int tile_x = (int)(((Math.Abs(local_y) % TILE_SCALE_FACTOR) / TILE_SCALE_FACTOR) * zoom);
int tile_y = (int)(((Math.Abs(local_x) % TILE_SCALE_FACTOR) / TILE_SCALE_FACTOR) * zoom);
float draw_x = 0;
float draw_y = 0;
if (local_y <= 0 && local_x <= 0)
{
draw_x = ((MM_WIDTH / 2) - tile_x) - zoom;
draw_y = ((MM_HEIGHT / 2) - tile_y) - zoom;
}
else if (local_y >= 0 && local_x <= 0)
{
draw_x = ((int)((MM_WIDTH) / 2) + (tile_x - zoom)) - zoom;
draw_y = ((int)((MM_HEIGHT) / 2) - tile_y) - zoom;
}
else if (local_y <= 0 && local_x >= 0)
{
draw_x = ((int)((MM_WIDTH) / 2) - tile_x) - zoom;
draw_y = ((int)((MM_HEIGHT) / 2) + (tile_y - zoom)) - zoom;
}
else if (local_y >= 0 && local_x >= 0)
{
draw_x = ((int)((MM_WIDTH) / 2) + (tile_x - zoom)) - zoom;
draw_y = ((int)((MM_HEIGHT) / 2) + (tile_y - zoom)) - zoom;
}
G.DrawImage(nearby_mm, draw_x, draw_y);
return nearby_mm;
}
That's it! You will need to find the player X, Y, and current continent yourself. There are plenty of resources on how to do that on this site. Once you have them, you can do something like this:
Code:
float zoom_factor = 1.0f; // 1.0f to 5.0f are typical values, where larger is more magnified
float zoom = (int)(TILE_HEIGHT * (float)(zoom_factor / 0.5f));
Bitmap out = new Bitmap(MM_WIDTH, MM_HEIGHT);
float player_x = GetPlayerX(); // you implement this
float player_y = GetPlayerY(); // you implement this
int current_continent = GetCurrentContinent(); // you implement this
DrawMinimapOverlay(GetNearbyMinimap(player_x, player_y, zoom, current_continent), out, player_x, player_y, zoom);
pb.Image = out; // pb is a PictureBox
pb.Update();
You should take some time to make this more efficient by caching calls to GetNearbyMinimap() if you are going to call it in a loop.
mm.jpg