254 lines
7.1 KiB
Markdown
254 lines
7.1 KiB
Markdown
# Map region system
|
|
|
|
A coordinate system is used to navigate through the RuneScape
|
|
world.
|
|
That coordinate system is based upon three variables, the absolute
|
|
X-, Y-, and Z-coordinates.
|
|
|
|
## Definitions
|
|
|
|
A **tile** is the in-game representation of an absolute coordinate.
|
|
|
|
e.g. The coordinates `(3222, 3222)` represents one tile in the heart
|
|
of the Lumbridge castle.
|
|
|
|
A **chunk of tiles**, 8 x 8 in size.
|
|
Also known as a region before the scope of a region was understood.
|
|
The chunk is considered a point so it has X and Y coordinates.
|
|
There are two forms of a Chunk: formatted and non-formatted,
|
|
a formatted chunks equation is:
|
|
|
|
```java
|
|
int chunkX = (getAbsoluteX() >> 3) - 6;
|
|
int chunkY = (getAbsoluteY() >> 3) - 6;
|
|
```
|
|
|
|
This centers the chunk on the map, more on that later.
|
|
|
|
The normal chunk equation is:
|
|
|
|
```java
|
|
int chunkX = (getAbsoluteX() >> 3);
|
|
int chunkY = (getAbsoluteY() >> 3);
|
|
```
|
|
|
|
e.g. For the coordinates `(3211, 3424)`, chunk X (formatted) is 395 and
|
|
the chunk Y (un-formatted) is 428.''
|
|
|
|
## Region
|
|
|
|
A **region** is 64 x 64 in size, or 8 x 8 in chunks.
|
|
The region is considered a point so it has X and Y coordinates.
|
|
|
|
The equation for finding the region the coordinates is within is:
|
|
|
|
```java
|
|
int regionX = (getUnformattedRegionX() >> 3); // getUnformatedRegionX() / 8;
|
|
int regionY = (getUnformattedRegionY() >> 3); // getUnformatedRegionY() / 8;
|
|
```
|
|
|
|
e.g. For the coordinates `(3211, 3424)`, region X is 50, and the
|
|
region Y is 53.
|
|
|
|
Note: The Region X and Region Y coordinates are traditionally not used
|
|
in server location calculations; but practical region systems should use
|
|
this calculation for many purposes.
|
|
|
|
## Map
|
|
|
|
There is no calculation for a map, and there is no Map X or Map Y.
|
|
|
|
A Map is, however, a 104 x 104 area made up of 13 x 13 chunks.
|
|
Why is the number not even you may ask?
|
|
Because it has a center.
|
|
The `(7, 7)` map chunk of the map is the center, and is also the formatted
|
|
chunk.
|
|
|
|
When a region update is called by the server, a new map is called, but you
|
|
must understand that the formatted chunk never changes; the tiles in the
|
|
map, however, are updated and trimmed.
|
|
When the player moves out of the formatted chunk, the map is re-positioned
|
|
to make that chunk the center yet again.
|
|
As stated, a new update is not needed every time the player enters a
|
|
new region, but when the range of +- 32 from the point in the center of
|
|
the chunk is reached, an update is required to update the map to the
|
|
new objects so that the 'black space' or fog is not reached. Confused?
|
|
|
|
## Diagram
|
|
|
|
![Diagram](./img/map-region-system-diagram.png)
|
|
|
|
The active chunk is the chunk in which the player resides.
|
|
The definite rendering chunks are the chunks in which will be rendered
|
|
on the players screen no matter where they are in the active chunk.
|
|
The indefinite rendering chunks are the chunks in which depending on
|
|
where the player is within the active chunk they may be rendered or not.
|
|
Remember this depends on the +- distance of 32 from the players
|
|
absolute position. The queue chunks are pre-loaded chunks in whichafter the
|
|
active chunk is moved may be disposed of or activated depending upon the
|
|
direction in which the active chunk changes.
|
|
|
|
## Loading
|
|
|
|
The following were the regions loaded for the coordinates `(3183, 3217)`:
|
|
|
|
![Loading diagram](./img/map-region-system-loading.png)
|
|
|
|
If you can imagine a puzzle, a 64 x 64 piece does not fit equally within
|
|
the 104 x 104 area.
|
|
So, bits of each region are taken that are within the 104 x 104 map area.
|
|
|
|
The amount of regions that are to be loaded can be calculated this way:
|
|
|
|
Please note that Region X and Region Y are not formatted.
|
|
|
|
```java
|
|
int amt = 0;
|
|
|
|
for (int i21 = (player.getLocation().getRegionX() - 6) / 8; i21 <= (player.getLocation().getRegionX() + 6) / 8; i21++) {
|
|
for (int k23 = (player.getLocation().getRegionY() - 6) / 8; k23 <= (player.getLocation().getRegionY() + 6) / 8; k23++)
|
|
amt++;
|
|
}
|
|
```
|
|
|
|
Along with this, the base X and base Y of each of the region can be
|
|
calculated:
|
|
|
|
```java
|
|
for (int i21 = (player.getLocation().getRegionX() - 6) / 8; i21 <= (player.getLocation().getRegionX() + 6) / 8; i21++) {
|
|
for (int k23 = (player.getLocation().getRegionY() - 6) / 8; k23 <= (player.getLocation().getRegionY() + 6) / 8; k23++)
|
|
System.out.println(i21 + " X " + (i21 << 6) + "," + k23 + " Y " + (k23 << 6));;;
|
|
}
|
|
```
|
|
|
|
The 'X' and 'Y' coordinates represents the coordinates of the region as
|
|
depicted in the diagram. After the regions are loaded they are trimmed
|
|
to the tiles that are necessary.
|
|
|
|
## Example Location class
|
|
|
|
The following is an example Location class written by sinisoul.
|
|
It is not heavily commented due to it being a very simple class,
|
|
and it should be easy to follow if you read this section in full.
|
|
|
|
```java
|
|
package net.forge.content.world.node;
|
|
|
|
/**
|
|
* RuneForge (317)
|
|
* @version 1.0.0
|
|
* @author SiniSoul (SiniSoul@live.com)
|
|
*/
|
|
public final class Location {
|
|
|
|
/**
|
|
* The Tile X and Y coordinates.
|
|
*/
|
|
private int tilex = 0,
|
|
tiley = 0;
|
|
|
|
/**
|
|
* The Height of the location.
|
|
*/
|
|
private int height = 0;
|
|
|
|
/**
|
|
* The asynchronous Chunk X and Y coordinates; used in region updating.
|
|
*/
|
|
private int chunkx = 0,
|
|
chunky = 0;
|
|
|
|
public void setTileX(int tilex) {
|
|
this.tilex = (tilex & 0xFFFF);
|
|
}
|
|
|
|
public int getTileX() {
|
|
return tilex;
|
|
}
|
|
|
|
public void setTileY(int tiley) {
|
|
this.tiley = (tiley & 0xFFFF);
|
|
}
|
|
|
|
public int getTileY() {
|
|
return tiley;
|
|
}
|
|
|
|
/**
|
|
* @param formatted If the chunk is formatted for map positioning or
|
|
* other formatted chunk comparison.
|
|
*/
|
|
public int calculateChunkX(boolean formatted) {
|
|
return formatted ? (getTileX() >> 3) - 6 : (getTileX() >> 3);
|
|
}
|
|
|
|
/**
|
|
* @param formatted If the chunk is formatted for map positioning or
|
|
* other formatted chunk comparison.
|
|
*/
|
|
public int calculateChunkY(boolean formatted) {
|
|
return formatted ? (getTileY() >> 3) - 6 : (getTileY() >> 3);
|
|
}
|
|
|
|
public void updateChunkX() {
|
|
this.chunkx = calculateChunkX(true);
|
|
}
|
|
|
|
public void updateChunkY() {
|
|
this.chunkx = calculateChunkY(true);
|
|
}
|
|
|
|
public int getChunkX() {
|
|
return chunkx;
|
|
}
|
|
|
|
public int getMapLocalX() {
|
|
return getTileX() - (getChunkX() << 3);
|
|
}
|
|
|
|
public int getChunkY() {
|
|
return chunky;
|
|
}
|
|
|
|
public int getMapLocalY() {
|
|
return getTileX() - (getChunkY() << 3);
|
|
}
|
|
|
|
public void setHeight(int height) {
|
|
this.height = (height & 0x3);
|
|
}
|
|
|
|
public int getHeight() {
|
|
return height;
|
|
}
|
|
|
|
public int getRegionX() {
|
|
return calculateChunkX(false) >> 3;
|
|
}
|
|
|
|
public int getRegionLocalX() {
|
|
return getTileX() - (getRegionX() << 6);
|
|
}
|
|
|
|
public int getRegionY() {
|
|
return calculateChunkY(false) >> 3;
|
|
}
|
|
|
|
public int getRegionLocalY() {
|
|
return getTileY() - (getRegionY() << 6);
|
|
}
|
|
|
|
public void set(int tilex, int tiley, int height) {
|
|
setTileX(tilex);
|
|
setTileY(tiley);
|
|
setHeight(height);
|
|
}
|
|
|
|
public Location(int tilex, int tiley, int height) {
|
|
set(tilex, tiley, height);
|
|
updateChunkX();
|
|
updateChunkY();
|
|
}
|
|
}
|
|
```
|