This describes how minimap was created, focusing on algotihms, and calculation.
Here is also explained, what is sent to client in order to be able to draw the minimap, and also resources related to minimap.
So first of all: what is that we want?
We want small minimap in upper left corner of the screen, displaying minified version of the world map.
We want it to be always updated with respect to the player's position.
Also, we require to display there monsters (as dots), players, dropped items, interact-able objects, and also the terrain.
Here is image displaying already implemented minimap (this is from game version dev. 1.7.2)
We will divide the map to 3 parts:
Is draw-able canvas, which has size equal to the minimal required size to fit there entire minified version of the map
In following image you can see the canvas displayed (obviously, it is too large to fir window), and is absolutely positioned.
Then, there is visible small rectangle, which will be the map viewport, and will have overflow hidden
If we want to move the map later on, we will just adjust the top / left arguments in css for the canvas,
and we will in viewport see that the map is moving.
This canvas is drawn upon only once, and then it is updated only when map transition happens.
Dynamic map data related canvas is canvas with the viewport size, and will be constantly redrawn in order to display all
players / monsters / dropped items.
In following example image you can see the dynamic canvas
Player overlay part is another canvas, with same size as the dynamic one,
and there is drawn only a dot, representing the player in the middle.
This canvas is drawn upon only once, and then it is never updated.
The data required in order to draw the minimap is only the data that is required to draw the large map.
There is really nothing else that would have to be sent as extra data from the server.
The static data portion is drawn, whenever the map static data is received from server, OR when player transitions, and
static data is already cached (then the cached data is used to draw the minimap static part).
The dynamic part is drawn repetitivelly when the entire game is drawn, updating the minimap to always up-to-date state
In order to draw the static part, we need to know, how large
the canvas needs to be to fit all the tiles.
For that we use floortiles to determine this, UNDERSTANDING, THAT IF IN MAP ARE PATHNODES, BUT NO FLOORTILES, THIS WILL BE UNDEFINED BEHAVIOR (meaning that map will be displayed incorrectly...). On the other hand, there are not going to be such cases, and if so, we can always implement patch for that...
Because virtual array indexes are very difficult to work with in any way, for calculation they are converted to the rela world coordinates, and we work with them.
Note: because of the way how virtual array indexes work, is is IMPOSSIBLE to calculate bounding rectangle only by working with VAI, and not converting it to the real world coordinates at all!!!
So, we iterate all available floortiles in the map, immediatelly convert VAI to real world coordinates, and save smallest, largest X and Y found.
Then, we create rectangle
var boundingRect = new Rectangle(smallestXPixels, largestYPixels, largestXPixels - smallestXPixels, largestYPixels - smallestYPixels);
Having the size of static map canvas, we need to draw a tile, having virtual array index.
For that, we must find / supply mapping function, which will project the virual array index to the draw coordinates.
We use the bounding rectangle x and y position as an offset.
The thing is, that if we would draw virtual array index \([0,0]\) to the upper left corner of the canvas, then drawing \([-1,0]\) would fail, since it would be drawn outside of the canvas, and therefore, not visible.
For that, we use the bounding rectangle x and y as a draw offset (ofcourse with additional calculation) and that suffies and the tile will be drawn correctly.
If we can correctly draw one tile, then obviously we can iteratively draw entire map...
In case of minimap, no textures are used what so ever, mainly for performance reasons, and secondly, because they are not needed.
Instead of that, we define for each tile ID a color, and then we draw a tile (specific pixels) coloured.
So for example, if floortile of ID \(1\) is a grass texture, we will draw in minimap with color \(green\).
These colors are hardcoded on the client side, and loaded to container in TextureDatabase. We can alo use a function which will return us the color, if we specify the floortile ID.
This approach can be used for literally everything in the minimap...