Roofed areas and better map engine

Map engine and Tile object

Our new map will make use of two new classes; TileMap, which stores and manages our loaded map data, and Tile, which stores information for each map tile. Lets look at the Tile class first:

function Tile(tx, ty, tt)
{
	this.x			= tx;
	this.y			= ty;
	this.type		= tt;
	this.roof		= null;
	this.roofType	= 0;
	this.eventEnter	= null;
}

A tile object is created with 3 arguments: tx, ty: the position of the tile on the map, and tt: the id of the tileType entry of the tile at this position.

This will populate the Tile objects x, y and type parameters accordingly. The object also has the properties roof and roofType for handling roofed areas (roof is a reference to the roof object of which this tiles roof is a part, or null if there is no roof here, and roofType is the id of the corresponding entry for the roof graphic to use in the tileTypes array.

Additionally, Tile objects have a eventEnter property; this can be a pointer to a function (or anonymous function) to execute when a character has completed moving on to this tile, or null if this tile does not have any special events attached.

The TileMap class

The TileMap class will become our map handling class, and currently has 3 properties: w and h, the width and height of the map, and map, an array which will contain all the Tile objects that make up our map.

function TileMap()
{
	this.map	= [];
	this.w		= 0;
	this.h		= 0;
}

This class will have a buildMapFromData method that will take 3 arguments; d, an array containing the tileType id to use for each map tile, and w, h, the dimensions of the map. The method begins by setting the w, h properties of the map object to correspond to the passed dimensions:

TileMap.prototype.buildMapFromData = function(d, w, h)
{
	this.w		= w;
	this.h		= h;

Next, we do a quick check to ensure the length of the d argument is equal to w x h, and if not we return false. After, we clear the map property of any data it currently contains.

	if(d.length!=(w*h)) { return false; }
	
	this.map.length	= 0;

We'll then loop through the d array, by row (y), and then each column of each row (x), and add a new Tile object to the map array created with the x, y position from the current loop iterations and the corresponding entry from the d array as the tileType id:

	for(var y = 0; y < h; y++)
	{
		for(var x = 0; x < w; x++)
		{
			this.map.push( new Tile(x, y, d[((y*w)+x)]) );
		}
	}

Once we're done we can return true and close the method:

	return true;
};

We'll also add a method called addRoofs which will take an array of roof objects as its argument. The method will begin by looping through each entry in the array, and for readabilities sake create a reference, r, to the current roof object:

TileMap.prototype.addRoofs = function(roofs)
{
	for(var i in roofs)
	{
		var r = roofs[i];

We'll also do some checks with the current roof object; we'll check it does not start or end outside of map bounds, and check its data array length is equal to its w x h. If it does not pass these checks, we'll skip this roof and not use it:

		if(r.x < 0 || r.y < 0 || r.x >= this.w || r.y >= this.h ||
			(r.x+r.w)>this.w || (r.y+r.h)>this.h ||
			r.data.length!=(r.w*r.h))
		{
			continue;
		}

We'll then loop through all the rows (y) and columns (x) of the roof, and calculate the tileIdx of the corresponding map position by adding the x, y offsets of the roof position to the x, y roof position we're looking at.

		for(var y = 0; y < r.h; y++)
		{
			for(var x = 0; x < r.w; x++)
			{
				var tileIdx = (((r.y+y)*this.w)+r.x+x);

We'll then update the Tile object at tileIdx in the map array to set its roof property to a reference to the current roof we're adding to the map, and the roofType for the Tile to the value in the data array from this roof. After doing so, we can close the nested loops and the method itself!

				this.map[tileIdx].roof = r;
				this.map[tileIdx].roofType = r.data[((y*r.w)+x)];
			}
		}
	}
};
Page loaded in 0.01 second(s).