API Docs for: 1.4.0
Show:

File: src\gameobjects\tilemap\TileMap.ts

/**
* 
* @module GameObjects
* @submodule Tilemap
* @main Tilemap
*/

module Kiwi.GameObjects.Tilemap { 

	/**
	* A TileMap handles the creation of TileMapLayers and the TileTypes that they use.
	* Since a TileMap isn't a Entity itself you cannot add it to the Stage inorder to render that it manages, 
	* Instead you have to add each layer lies within it. This way you can have other GameObjects behind/in-front of layers.
	* 
	* @class TileMap
	* @namespace Kiwi.GameObjects.Tilemap
	* @constructor 
	* @param state {Kiwi.State} The state that this Tilemap is on.
	* @param [tileMapDataKey] {String} The Data key for the JSON you would like to use.
	* @param [atlas] {Kiwi.Textures.TextureAtlas} The texture atlas that you would like the tilemap layers to use.
	* @param [startingCell=0] {number} The number for the initial cell that the first TileType should use. See 'createFromFileStore' for more information.
	* @return {TileMap}
	*/
	export class TileMap {

		constructor(state: Kiwi.State, tileMapData?: any, atlas?: Kiwi.Textures.TextureAtlas, startingCell:number=0) {

			this.tileTypes = [];
			this.createTileType(-1);
			this.layers = [];

			this.state = state;
			this.game = state.game;

			if (tileMapData !== undefined && atlas !== undefined) {
				this.createFromFileStore(tileMapData, atlas, startingCell);
			} else if (tileMapData !== undefined || atlas !== undefined) {
				Kiwi.Log.warn('You must pass BOTH the TileMapDataKey and TextureAtlas inorder to create a TileMap from the File Store.', '#tilemap');
			}

		}

		/**
		* The orientation of the tilemap. 
		* Note: This value does not affect the individual layers. 
		* 
		* @property orientation
		* @type String
		* @public
		*/
		public orientation: string;

		/**
		* Is an Array containing all of the TileTypes that are available on the TileMap.
		* @property tileTypes
		* @type TileType[]
		* @public
		*/
		public tileTypes: TileType[];

		/**
		* A list of all of the TileMapLayers that exist on the TileMap.
		* @property layers
		* @type TileMapLayerBase
		* @public
		*/
        public layers: TileMapLayer[];

		/**
		* The state that this TileMap exists on.
		* @property state
		* @type Kiwi.State
		* @public
		*/
		public state: Kiwi.State;

		/**
		* The game that this TileMap is a part of.
		* @property game
		* @type Kiwi.Game
		* @public
		*/
		public game: Kiwi.Game;

		/**
		* The default width of a single tile that a TileMapLayer is told to have upon its creation. 
		* @property tileWidth
		* @type Number
		* @default 0
		* @public
		*/
		public tileWidth: number = 0;

		/**
		* The default height of a single tile that a TileMapLayer is told to have upon its creation.
		* @property tileHeight
		* @type Number
		* @default 0
		* @public
		*/
		public tileHeight: number = 0;

		/**
		* The default width of all TileMapLayers when they are created. 
		* This value is in Tiles.
		* @property width
		* @type Number
		* @default 0
		* @public
		*/
		public width: number = 0;

		/**
		* The default height of all TileMapLayers when they are created. 
		* This value is in Tiles.
		* @property height
		* @type Number
		* @default 0
		* @public
		*/
		public height: number = 0;

		/**
		* The width of the tilemap in pixels. This value is READ ONLY.
		* @property widthInPixels
		* @type Number
		* @public
		*/
		public get widthInPixels(): number {
			return this.width * this.tileWidth;
		}

		/**
		* The height of the tilemap in pixels. This value is READ ONLY.
		* @property heightInPixels
		* @type Number
		* @public
		*/
		public get heightInPixels(): number {
			return this.height * this.tileHeight;
		}

		/**
		* Any properties that were found in the JSON during creation.
		* @property properties
		* @type Object
		* @public
		*/
		public properties: any = {};

		/** 
		* Creates new tilemap layers from a JSON file that you pass (has to be in the Tiled Format).
		* The texture atlas you pass is that one that each TileMapLayer found in the JSON will use, You can change the TextureAtlas afterwards.
		* New TileTypes will automatically be created. The number is based on the Tileset parameter of the JSON. 
		* The cell used for new TileTypes will begin at 0 and increment each time a new TileType is created (and a cell exists). Otherwise new TileTypes will start will a cell of -1 (none).
		* @method createFromFileStore
		* @param tileMapData {Any} This can either 
		* @param atlas {Kiwi.Textures.TextureAtlas} The texture atlas that you would like the tilemap layers to use.
		* @param [startingCell=0] {number} The number for the initial cell that the first TileType should use. If you pass -1 then no new TileTypes will be created.
		* @public
		*/
		public createFromFileStore(tileMapData: any, atlas: Kiwi.Textures.TextureAtlas, startingCell:number=0) {

            var json = null;

            if (Kiwi.Utils.Common.isString(atlas)) {
                atlas = this.state.textures[<any>atlas];
            }

			//Does the JSON exist?
			switch (typeof tileMapData) {
				case 'string':
					if (this.game.fileStore.exists(tileMapData) == false) {
						Kiwi.Log.error('The JSON file you have told to use for a TileMap does not exist.', '#tilemap', '#json');
						return false;
					}   

					json = JSON.parse( this.game.fileStore.getFile(tileMapData).data );
					break;

                case 'object':
                    
                    //Is it a KiwiJS file?
                    if (tileMapData.isData && tileMapData.dataType === Kiwi.Files.File.JSON) {

                        if ( Kiwi.Utils.Common.isString(tileMapData.parse) ) {
                            json = JSON.parse(tileMapData.data);
                        } else {
                            json = tileMapData.data;
                        }

                    } else {
                        json = tileMapData;
                    }

					break;

				default:
					Kiwi.Log.error('The type of TileMapData passed could not be idenified. Please either pass a name of JSON file to use OR an object to be used.', '#tilemap');
			}

			//Get the map information
			this.orientation = (json.orientation == undefined) ? ORTHOGONAL : json.orientation;
			this.tileWidth = (json.tilewidth == undefined) ? 32 : json.tilewidth;
			this.tileHeight = (json.tileheight == undefined) ? 32 : json.tileheight;
			this.width = json.width;
			this.height = json.height;


			//Add the properties
			for (var prop in json.properties) {
				this.properties[prop] = json.properties[prop];
			}


			//Generate the Tiles needed.
			if (json.tilesets !== "undefined" && startingCell !== -1) 
				this._generateTypesFromTileset(json.tilesets, atlas, startingCell);

			//Generate the layers we need
			for (var i = 0; i < json.layers.length; i++) {
				var layerData = json.layers[i];

				//Check what type it is.
				switch (json.layers[i].type) {
					case "tilelayer":
						var w = (layerData.width !== undefined) ? layerData.width : this.width;
						var h = (layerData.height !== undefined) ? layerData.height : this.height;

						var layer = this.createNewLayer(layerData.name, atlas, layerData.data, w, h, layerData.x * this.tileWidth, layerData.y * this.tileHeight );
						
						//Add the extra data...
						layer.visible = (layerData.visible == undefined) ? true : layerData.visible;
						layer.alpha = (layerData.opacity == undefined) ? 1 : layerData.opacity;
						if (layerData.properties !== undefined)layer.properties = layerData.properties;

						break;

					case "objectgroup":
						this.createNewObjectLayer();
						break;

					case "imagelayer":
						this.createNewImageLayer();
						break;
				}


			}

		}

		/**
		* Generates new TileTypes based upon the Tileset information that lies inside the Tiled JSON.
		* This is an INTERNAL method, which is used when the createFromFileStore method is executed.
		* @method _generateTypesFromTileset
		* @param tilesetData {Array} The tileset part of the JSON.
		* @param atlas {Kiwi.Textures.TextureAtlas} The Texture atlas which contains the cells that the new TileTypes will use.
		* @param startingCell {Number} The first cell number that would be used.
		* @private
		*/
		private _generateTypesFromTileset( tilesetData:any[], atlas:Kiwi.Textures.TextureAtlas, startingCell:number ) {

			//Loop through the tilesets
			for (var i = 0; i < tilesetData.length; i++) {

				var tileset = tilesetData[i];

				//Tileset Information
				var m = tileset.margin;
				var s = tileset.spacing; 
				var tw = tileset.tilewidth;
				var th = tileset.tileheight;
				var iw = tileset.imagewidth - m;
				var ih = tileset.imageheight - m;

				//Drawing offsets
				var offset = (tileset.tileoffset == undefined) ? { x: 0, y: 0 } : tileset.tileoffset;

				//Calculate how many tiles there are in this tileset and thus how many different tile type there can be.
				for (var y = m; y < ih; y += th) {
					for (var x = m; x < iw; x += tw) {

						//Does the cell exist? Then use that.
						var cell = (atlas.cells[startingCell] == undefined) ? -1 : startingCell ;  

						var tileType = this.createTileType(cell);
						tileType.offset.x = offset.x;
						tileType.offset.y = offset.y;

						startingCell++; //Increase the cell to use by one.
					}
				}

				//Add tile properties
				for (var tp in tileset.tileproperties) {
					var tileType = this.tileTypes[(parseInt(tileset.firstgid) + parseInt(tp))];
					tileType.properties = tileset.tileproperties[tp];
				}

			}

		}

		/**
		* Method to set the default TileMap properties. Useful when wanting to create tilemaps programmatically.
		* @method setTo
		* @param tileWidth {Number} The width of a single tile.
		* @param tileHeight {Number} The height of a single tile.
		* @param width {Number} The width of the whole map.
		* @param height {Number} The height of the whole map.
		* @public
		*/
		public setTo(tileWidth: number, tileHeight: number, width: number, height: number) {
			this.tileWidth = tileWidth;
			this.tileHeight = tileHeight;
			this.width = width;
			this.height = height;
		}

		/**
		*-----------------------
		* Creation of Tile Types
		*-----------------------
		**/

		/**
		* Generates a single new TileType. Returns the TileType that was generated.
		* @method createTileType
		* @param [cell=-1] {Number} The cell that is to be used. Default is -1 (which means none)
		* @return {TileType} The TileType generated.
		* @public
		*/
		public createTileType(cell:number = -1):TileType {
			var tileType = new TileType(this, this.tileTypes.length, cell);
			this.tileTypes.push(tileType);

			return tileType;
		}
		
		/**
		* Creates a new TileType for each cell that you pass.
		* @method createTileTypes
		* @param cells {Number[]} The cells that you want a new TileType created for.
		* @return {TileTypes[]} The TileTypes generated.
		* @public
		*/
		public createTileTypes(cells:number[]):TileType[] {
			var types = [];
			for (var i = 0; i < cells.length; i++) {

				types.push( this.createTileType( cells[i] ) );

			}
			return types;
		}

		/**
		* Used to create a number of TileTypes based starting cell number and how many you want from there.
		* @method createTileTypesByRange
		* @param cellStart {Number} The starting number of the cell.
		* @param range {Number} How many cells (from the starting cell) should be created.
		* @return {TileTypes[]} The TileTypes generated.
		*/
		public createTileTypesByRange(cellStart: number, range: number): TileType[]{
			var types = [];
			for (var i = cellStart; i <= cellStart + range; i++) {

				types.push( this.createTileType( i ) );

			}
			return types;
		}


		/**
		*-----------------------
		* Cell Modifications
		*-----------------------
		**/

		/** 
		* Changes a single cellIndex that a TileType is to use when it is rendered.
		* @method setCell
		* @param type {number} The number of the TileType that is to change.
		* @param cell {number} The new cellIndex it should have.
		* @public
		*/
		public setCell(type: number, cell: number) {
			this.tileTypes[type].cellIndex = cell;
		}

		/** 
		* Changes a range of cellIndexs for Tiles the same range of TileTypes. 
		* @method setCellsByRange
		* @param typeStart {number} The starting TileType that is to be modified.
		* @param cellStart {number} The starting cellIndex that the first TileType should have.
		* @param range {number} How many times it should run.
		* @public
		*/
        public setCellsByRange(typeStart: number, cellStart: number, range: number) {

            for (var i = typeStart; i < typeStart + range; i++) {
                this.tileTypes[i].cellIndex = cellStart;
                cellStart++;
            }

        }

		/**
		*-----------------------
		* Creation of Tilemap Layers
		*-----------------------
		**/

		/** 
		* Creates a new TileMapLayer with the details that are provided. 
		* If no width/height/tileWidth/tileHeight parameters are passed then the values will be what this TileMap has.
		* If no 'data' is provided then the map will be automatically filled with empty Types of Tiles.
		* Returns the new TileMapLayer that was created.
		* @method createNewLayer
		* @param name {String} Name of the TileMap.
		* @param atlas {Kiwi.Textures.TextureAtlas} The TextureAtlas that this layer should use.
		* @param data {Number[]} The tile information. 
		* @param [w=this.width] {Number} The width of the whole tile map. In Tiles.
		* @param [h=this.height] {Number} The height of the whole tile map. In Tiles.
		* @param [x=0] {Number} The position of the tilemap on the x axis. In pixels.
		* @param [y=0] {Number} The position of the tilemap on the y axis. In pixels.
		* @param [tw=this.tileWidth] {Number} The width of a single tile.
		* @param [th=this.tileHeight] {Number} The height of a single tile.
        * @param [orientation] {String} The orientation of the tilemap. Defaults to the same as this TileMap.
		* @return {TileMapLayer} The TileMapLayer that was created.
		* @public
		*/
        public createNewLayer(name: string, atlas: Kiwi.Textures.TextureAtlas, data: number[] = [], w: number = this.width, h: number = this.height, x: number = 0, y: number = 0, tw: number = this.tileWidth, th: number = this.tileHeight, orientation: string=this.orientation): TileMapLayer {

			//Did the user provide enough data?
			if (data.length < w * h) {

				//No... So push empty cells instead
				var i = data.length - 1;
				while (++i < w * h) {
					data.push(0);
				}
			}

			//Create the new layer
            var layer: TileMapLayer;

            if (orientation == ISOMETRIC) {
                layer = new Kiwi.GameObjects.Tilemap.TileMapLayerIsometric(this, name, atlas, data, tw, th, x, y, w, h);
            } else {
                layer = new Kiwi.GameObjects.Tilemap.TileMapLayerOrthogonal(this, name, atlas, data, tw, th, x, y, w, h);
            }

			//Add the new layer to the array
			this.layers.push(layer);

			return layer;
		}


		/**
		* Eventually will create a new object layer. Currently does nothing.
		* @method createNewObjectLayer
		* @public
		*/
		public createNewObjectLayer() {
			Kiwi.Log.log("OBJECT GROUP layers are currently not supported.", '#tilemap');
		}


		/**
		* Eventually will create a new image layer. Currently does nothing.
		* @method createNewObjectLayer
		* @public
		*/
		public createNewImageLayer() {
			Kiwi.Log.log("IMAGE layers are currently not supported.", '#tilemap');
		}

		/**
		*-----------------------
		* TileMapLayer Management Functions
		*-----------------------
		**/

		/**
		* Get a layer by the name that it was given upon creation.
		* Returns null if no layer with that name was found.
		* @method getLayerByName
		* @param name {String} Name of the layer you would like to select.
		* @return {TileMapLayer} Either the layer with the name passed, or null if no Layer with that name was found.
		* @public
		*/
        public getLayerByName(name: string): TileMapLayer {
			for (var i = 0; i < this.layers.length; i++) {
				if (this.layers[i].name == name) {
					return this.layers[i];
				}
			}
			return null;
		}

		/**
		* Returns the layer with the number associated with it in the layers array.
		* @method getLayer
		* @param num {Number} Number of the Layer you would like to get.
		* @return {TileMapLayer}
		* @public
		*/
        public getLayer(num: number): TileMapLayer {
			return (this.layers[num] !== undefined) ? this.layers[num] : null;
		}


		/**
		* The type of object that it is.
		* @method objType
		* @return {String} "TileMap"
		* @public
		*/
		public objType() {
			return "TileMap";
		}

	}

	export var ISOMETRIC: string = "isometric";

	export var ORTHOGONAL: string = "orthogonal";

}