Minimap Dot Tiles
Minimap dots and how to deal with them are a mystery to many scripters, so in this tutorial I will share some knowledge I gained when trying to gather the exact coordinates of a minimap dot.
Dealing with minimap dots is an integral part of many scripts. For example, it happens whenever doing anything with ground items, or when finding NPC’s. In fact, the code I’m about to show happens whenever you are detecting NPC’s on the mainscreen with the NPCV2 system. Lookup the function TRSNPCV2.GetCuboidArray
for reference!
Anyways, enough foreshadowing, let’s get into it. In this tutorial, we will be finding the outlines of the tiles on which there are Potato Cacti inside the Kalphite Lair. These ones for reference:
Below you see the final product we are working towards. Feel free to copy the code at the bottom of the tutorial and play around with it yourself.
How do we get the right coordinates of the dots on the minimap?
This is of course the main problem to solve. Look closely at the picture below to exactly know the problem. Starting from the white dot, i.e. the player, we can draw a grid of pixels where we expect tiles to have their coordinates. What exactly this means is: if you put in each of the green minimap coordinates into the TRSMap.MM2MAP
function, you should get a coordinate on the map that is representative of a tile. Coordinates that are what I call ‘representative’ of a tile, are the coordinates you would get back from Map.Position()
if you stand on that tile. The way we get those green points is by rotating the coordinates of the pixels in steps of 4 away from the center of the minimap with the angle of the compass. But as you can see, the fact that we are stuck on a grid of pixels introduces quite some rounding error after the rotation, so we really want that grid to be a bit more precise. We do that by instead of using
TPoint
’s, we use Vector3
’s instead, which allow for decimal values in their coordinates, unlike TPoint
‘s. For visual demonstration purposes I converted them to TPoint
’s again, but in the code you wouldn’t want to do this.
Now next, on the same image, notice the purple pixels above the red minimap dots. They are the coordinates that the Minimap.GetDots(ERSMinimapDot.ITEM);
returns. As you can see, they are kinda in the middle between the green dots, and it is not immediately clear from these coordinates which tile the dot is on. The green dot that the item actually belongs to is always the one to the bottom left of it. So, how do we get that specific green dot? We offset the items dots by [2,1]
and then we get near enough to the green point that if we snap the dot to the nearest green dot, we always get the right one. This is done with the line dotVecs[i] := mmGrid.NearestVec(dotVecs[i]);
Now that we have the ‘right’ minimap coordinate, we can convert it to a TPA and then use that coordinate in TRSMAP.MM2MAP
to get a world coordinate that is at max one pixel away from the representative tile (the rounding to TPA and rotating it again in MM2Map
introduces some errors again), and if we are only one pixel away, we can easily normalize it to the nearest tile coordinate with RSTranslator.NormalizeNearestTile(dot);
and then if we use Map.GetTileMS
on this coordinate, we get the exact rectangle we need, as you can see in the gif. Enjoy!
Side note: this also works on the old walker, but you cannot do the final normalization, so you will still have slight imprecision of around 1/4 of a tile.
{$I Wasplib/osr.simba}
function GetMinimapGrid(angle: Single): Vector3Array;
var x, y: Integer;
begin
for x := Minimap.Center().X - 20 * 4 to Minimap.Center().X + 20 * 4 with 4 do
for y := Minimap.Center().Y - 20 * 4 to Minimap.Center().Y + 20 * 4 with 4 do
Result += Vec3(x, y).RotateXY(angle, Minimap.Center().X, Minimap.Center.Y);
end;
function DotsToTile(dots: TPointArray; angle: Single): TPointArray;
var
dotVecs, mmGrid: Vector3Array;
i: Integer;
begin
dotVecs.FromTPA(dots);
dotVecs := dotVecs.Offset([2,1]); //This can be either 2,1 or 2,2
mmGrid := GetMinimapGrid(angle);
for i := 0 to High(dotVecs) do
dotVecs[i] := mmGrid.NearestVec(dotVecs[i]);
Result := dotVecs.ToTPA();
end;
var
mmGrid: Vector3Array;
mmGridTPA, dots: TPointArray;
dot: TPoint;
rect: TRectangle;
angle: Single;
bmp: TMufasaBitmap;
begin
RSClient.RemoteInput.EnableRealInput();
Map.SetupChunks([[[53,149,55,147],[2]]]); //This is where the kalphites are on the MejRS map
while True do
begin
angle := Minimap.GetCompassAngle(False); //gets angle in radians
mmGrid := GetMinimapGrid(angle);
dots := Minimap.GetDots(ERSMinimapDot.ITEM); // if you want NPC's as well you can do dots := Minimap.GetDots([ERSMinimapDot.ITEM,ERSMinimapDot.NPC]);
dots := dotsToTile(dots, angle);
bmp.FromClient();
bmp.DrawTPA(mmGrid.ToTPA, $FF00);
bmp.DrawTPA(dots, $FF00FF);
// translate dot coordinates to World coordinates and then to a rectangle on the mainscreen
for dot in dots do
begin
dot := Map.MM2Map(dot, angle);
dot := RSTranslator.NormalizeNearestTile(dot);
rect := Map.GetTileMS(dot);
bmp.DrawRect(rect, $FF);
end;
bmp.Debug();
Wait(500);
end;
end.