1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2008-2010 Ricardo Quesada
  4  Copyright (c) 2011      Zynga Inc.
  5 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25  ****************************************************************************/
 26 
 27 /**
 28  * <p>cc.TMXLayer represents the TMX layer. </p>
 29  *
 30  * <p>It is a subclass of cc.SpriteBatchNode. By default the tiles are rendered using a cc.TextureAtlas. <br />
 31  * If you modify a tile on runtime, then, that tile will become a cc.Sprite, otherwise no cc.Sprite objects are created. <br />
 32  * The benefits of using cc.Sprite objects as tiles are: <br />
 33  * - tiles (cc.Sprite) can be rotated/scaled/moved with a nice API </p>
 34  *
 35  * <p>If the layer contains a property named "cc.vertexz" with an integer (in can be positive or negative), <br />
 36  * then all the tiles belonging to the layer will use that value as their OpenGL vertex Z for depth. </p>
 37  *
 38  * <p>On the other hand, if the "cc.vertexz" property has the "automatic" value, then the tiles will use an automatic vertex Z value. <br />
 39  * Also before drawing the tiles, GL_ALPHA_TEST will be enabled, and disabled after drawing them. The used alpha func will be:  </p>
 40  *
 41  * glAlphaFunc( GL_GREATER, value ) <br />
 42  *
 43  * <p>"value" by default is 0, but you can change it from Tiled by adding the "cc_alpha_func" property to the layer. <br />
 44  * The value 0 should work for most cases, but if you have tiles that are semi-transparent, then you might want to use a different value, like 0.5.</p>
 45  * @class
 46  * @extends cc.SpriteBatchNode
 47  *
 48  * @property {Array}                tiles               - Tiles for layer
 49  * @property {cc.TMXTilesetInfo}    tileset             - Tileset for layer
 50  * @property {Number}               layerOrientation    - Layer orientation
 51  * @property {Array}                properties          - Properties from the layer. They can be added using tilemap editors
 52  * @property {String}               layerName           - Name of the layer
 53  * @property {Number}               layerWidth          - Width of the layer
 54  * @property {Number}               layerHeight         - Height of the layer
 55  * @property {Number}               tileWidth           - Width of a tile
 56  * @property {Number}               tileHeight          - Height of a tile
 57  */
 58 cc.TMXLayer = cc.SpriteBatchNode.extend(/** @lends cc.TMXLayer# */{
 59 	tiles: null,
 60 	tileset: null,
 61 	layerOrientation: null,
 62 	properties: null,
 63 	layerName: "",
 64 
 65     //size of the layer in tiles
 66     _layerSize: null,
 67     _mapTileSize: null,
 68     //TMX Layer supports opacity
 69     _opacity: 255,
 70     _minGID: null,
 71     _maxGID: null,
 72     //Only used when vertexZ is used
 73     _vertexZvalue: null,
 74     _useAutomaticVertexZ: null,
 75     _alphaFuncValue: null,
 76     //used for optimization
 77     _reusedTile: null,
 78     _atlasIndexArray: null,
 79     //used for retina display
 80     _contentScaleFactor: null,
 81 
 82     _cacheCanvas:null,
 83     _cacheContext:null,
 84     _cacheTexture:null,
 85     // Sub caches for avoid Chrome big image draw issue
 86     _subCacheCanvas:null,
 87     _subCacheContext:null,
 88     _subCacheCount:0,
 89     _subCacheWidth:0,
 90     // Maximum pixel number by cache, a little more than 3072*3072, real limit is 4096*4096
 91     _maxCachePixel:10000000,
 92     _className:"TMXLayer",
 93     /**
 94      *  Constructor
 95      */
 96     ctor:function () {
 97         cc.SpriteBatchNode.prototype.ctor.call(this);
 98         this._descendants = [];
 99 
100         this._layerSize = cc.size(0, 0);
101         this._mapTileSize = cc.size(0, 0);
102 
103         if(cc._renderType === cc._RENDER_TYPE_CANVAS){
104             var locCanvas = cc._canvas;
105             var tmpCanvas = document.createElement('canvas');
106             tmpCanvas.width = locCanvas.width;
107             tmpCanvas.height = locCanvas.height;
108             this._cacheCanvas = tmpCanvas;
109             this._cacheContext = this._cacheCanvas.getContext('2d');
110             var tempTexture = new cc.Texture2D();
111             tempTexture.initWithElement(tmpCanvas);
112             tempTexture.handleLoadedTexture();
113             this._cacheTexture = tempTexture;
114             this.width = locCanvas.width;
115 	        this.height = locCanvas.height;
116 	        // This class uses cache, so its default cachedParent should be himself
117 	        this._cachedParent = this;
118         }
119     },
120 
121     /**
122      * Sets the untransformed size of the TMXLayer.
123      * @override
124      * @param {cc.Size|Number} size The untransformed size of the TMXLayer or The untransformed size's width of the TMXLayer.
125      * @param {Number} [height] The untransformed size's height of the TMXLayer.
126      */
127     setContentSize:function (size, height) {
128         var locContentSize = this._contentSize;
129 	    cc.Node.prototype.setContentSize.call(this, size, height);
130 
131         if(cc._renderType === cc._RENDER_TYPE_CANVAS){
132             var locCanvas = this._cacheCanvas;
133             var scaleFactor = cc.CONTENT_SCALE_FACTOR();
134             locCanvas.width = 0 | (locContentSize.width * 1.5 * scaleFactor);
135             locCanvas.height = 0 | (locContentSize.height * 1.5 * scaleFactor);
136 
137             this._cacheContext.translate(0, locCanvas.height);
138             var locTexContentSize = this._cacheTexture._contentSize;
139             locTexContentSize.width = locCanvas.width;
140             locTexContentSize.height = locCanvas.height;
141 
142             // Init sub caches if needed
143             var totalPixel = locCanvas.width * locCanvas.height;
144             if(totalPixel > this._maxCachePixel) {
145                 if(!this._subCacheCanvas) this._subCacheCanvas = [];
146                 if(!this._subCacheContext) this._subCacheContext = [];
147 
148                 this._subCacheCount = Math.ceil( totalPixel / this._maxCachePixel );
149                 var locSubCacheCanvas = this._subCacheCanvas, i;
150                 for(i = 0; i < this._subCacheCount; i++) {
151                     if(!locSubCacheCanvas[i]) {
152                         locSubCacheCanvas[i] = document.createElement('canvas');
153                         this._subCacheContext[i] = locSubCacheCanvas[i].getContext('2d');
154                     }
155                     var tmpCanvas = locSubCacheCanvas[i];
156                     tmpCanvas.width = this._subCacheWidth = Math.round( locCanvas.width / this._subCacheCount );
157                     tmpCanvas.height = locCanvas.height;
158                 }
159                 // Clear wasted cache to release memory
160                 for(i = this._subCacheCount; i < locSubCacheCanvas.length; i++) {
161                     tmpCanvas.width = 0;
162                     tmpCanvas.height = 0;
163                 }
164             }
165             // Otherwise use count as a flag to disable sub caches
166             else this._subCacheCount = 0;
167         }
168     },
169 
170 
171     /**
172      * Return texture of cc.SpriteBatchNode
173      * @return {cc.Texture2D}
174      */
175 	getTexture: null,
176 
177     _getTextureForCanvas:function () {
178         return this._cacheTexture;
179     },
180 
181     /**
182      * don't call visit on it's children ( override visit of cc.Node )
183      * @override
184      * @param {CanvasRenderingContext2D} ctx
185      */
186     visit: null,
187 
188     _visitForCanvas: function (ctx) {
189         var context = ctx || cc._renderContext;
190         // quick return if not visible
191         if (!this._visible)
192             return;
193 
194         context.save();
195         this.transform(ctx);
196         var i, locChildren = this._children;
197 
198         if (this._cacheDirty) {
199             //
200             var eglViewer = cc.view;
201             eglViewer._setScaleXYForRenderTexture();
202             //add dirty region
203             var locCacheContext = this._cacheContext, locCacheCanvas = this._cacheCanvas;
204             locCacheContext.clearRect(0, 0, locCacheCanvas.width, -locCacheCanvas.height);
205             locCacheContext.save();
206             locCacheContext.translate(this._anchorPointInPoints.x, -(this._anchorPointInPoints.y));
207             if (locChildren) {
208                 this.sortAllChildren();
209                 for (i = 0; i < locChildren.length; i++) {
210                     if (locChildren[i])
211                         locChildren[i].visit(locCacheContext);
212                 }
213             }
214             locCacheContext.restore();
215             // Update sub caches if needed
216             if(this._subCacheCount > 0) {
217                 var subCacheW = this._subCacheWidth, subCacheH = locCacheCanvas.height;
218                 for(i = 0; i < this._subCacheCount; i++) {
219                     this._subCacheContext[i].drawImage(locCacheCanvas, i * subCacheW, 0, subCacheW, subCacheH, 0, 0, subCacheW, subCacheH);
220                 }
221             }
222 
223             //reset Scale
224             eglViewer._resetScale();
225             this._cacheDirty = false;
226         }
227         // draw RenderTexture
228         this.draw(ctx);
229         context.restore();
230     },
231 
232     /**
233      * draw cc.SpriteBatchNode (override draw of cc.Node)
234      * @param {CanvasRenderingContext2D} ctx
235      */
236     draw:null,
237 
238     _drawForCanvas:function (ctx) {
239         var context = ctx || cc._renderContext;
240         //context.globalAlpha = this._opacity / 255;
241         var posX = 0 | ( -this._anchorPointInPoints.x), posY = 0 | ( -this._anchorPointInPoints.y);
242         var eglViewer = cc.view;
243         var locCacheCanvas = this._cacheCanvas;
244         //direct draw image by canvas drawImage
245         if (locCacheCanvas) {
246             var locSubCacheCount = this._subCacheCount, locCanvasHeight = locCacheCanvas.height * eglViewer._scaleY;
247             if(locSubCacheCount > 0) {
248                 var locSubCacheCanvasArr = this._subCacheCanvas;
249                 for(var i = 0; i < locSubCacheCount; i++){
250                     var selSubCanvas = locSubCacheCanvasArr[i];
251                     context.drawImage(locSubCacheCanvasArr[i], 0, 0, selSubCanvas.width, selSubCanvas.height,
252                         posX + i * this._subCacheWidth, -(posY + locCanvasHeight), selSubCanvas.width * eglViewer._scaleX, locCanvasHeight);
253                 }
254             } else{
255                 //context.drawImage(locCacheCanvas, 0, 0, locCacheCanvas.width, locCacheCanvas.height,
256                 //    posX, -(posY + locCacheCanvas.height ), locCacheCanvas.width, locCacheCanvas.height );
257                 context.drawImage(locCacheCanvas, 0, 0, locCacheCanvas.width, locCacheCanvas.height,
258                     posX, -(posY + locCanvasHeight), locCacheCanvas.width * eglViewer._scaleX, locCanvasHeight);
259             }
260         }
261     },
262 
263     /**
264      * @return {cc.Size}
265      */
266     getLayerSize:function () {
267         return cc.size(this._layerSize.width, this._layerSize.height);
268     },
269 
270     /**
271      * @param {cc.Size} Var
272      */
273     setLayerSize:function (Var) {
274         this._layerSize.width = Var.width;
275         this._layerSize.height = Var.height;
276     },
277 
278 	_getLayerWidth: function () {
279 		return this._layerSize.width;
280 	},
281 	_setLayerWidth: function (width) {
282 		this._layerSize.width = width;
283 	},
284 	_getLayerHeight: function () {
285 		return this._layerSize.height;
286 	},
287 	_setLayerHeight: function (height) {
288 		this._layerSize.height = height;
289 	},
290 
291     /**
292      * Size of the map's tile (could be different from the tile's size)
293      * @return {cc.Size}
294      */
295     getMapTileSize:function () {
296         return cc.size(this._mapTileSize.width,this._mapTileSize.height);
297     },
298 
299     /**
300      * @param {cc.Size} Var
301      */
302     setMapTileSize:function (Var) {
303         this._mapTileSize.width = Var.width;
304         this._mapTileSize.height = Var.height;
305     },
306 
307 	_getTileWidth: function () {
308 		return this._mapTileSize.width;
309 	},
310 	_setTileWidth: function (width) {
311 		this._mapTileSize.width = width;
312 	},
313 	_getTileHeight: function () {
314 		return this._mapTileSize.height;
315 	},
316 	_setTileHeight: function (height) {
317 		this._mapTileSize.height = height;
318 	},
319 
320     /**
321      * Pointer to the map of tiles
322      * @return {Array}
323      */
324     getTiles:function () {
325         return this.tiles;
326     },
327 
328     /**
329      * @param {Array} Var
330      */
331     setTiles:function (Var) {
332         this.tiles = Var;
333     },
334 
335     /**
336      * Tile set information for the layer
337      * @return {cc.TMXTilesetInfo}
338      */
339     getTileset:function () {
340         return this.tileset;
341     },
342 
343     /**
344      * @param {cc.TMXTilesetInfo} Var
345      */
346     setTileset:function (Var) {
347         this.tileset = Var;
348     },
349 
350     /**
351      * Layer orientation, which is the same as the map orientation
352      * @return {Number}
353      */
354     getLayerOrientation:function () {
355         return this.layerOrientation;
356     },
357 
358     /**
359      * @param {Number} Var
360      */
361     setLayerOrientation:function (Var) {
362         this.layerOrientation = Var;
363     },
364 
365     /**
366      * properties from the layer. They can be added using Tiled
367      * @return {Array}
368      */
369     getProperties:function () {
370         return this.properties;
371     },
372 
373     /**
374      * @param {Array} Var
375      */
376     setProperties:function (Var) {
377         this.properties = Var;
378     },
379 
380     /**
381      * Initializes a cc.TMXLayer with a tileset info, a layer info and a map info
382      * @param {cc.TMXTilesetInfo} tilesetInfo
383      * @param {cc.TMXLayerInfo} layerInfo
384      * @param {cc.TMXMapInfo} mapInfo
385      * @return {Boolean}
386      */
387     initWithTilesetInfo:function (tilesetInfo, layerInfo, mapInfo) {
388         // XXX: is 35% a good estimate ?
389         var size = layerInfo._layerSize;
390         var totalNumberOfTiles = parseInt(size.width * size.height);
391         var capacity = totalNumberOfTiles * 0.35 + 1; // 35 percent is occupied ?
392         var texture;
393         if (tilesetInfo)
394             texture = cc.textureCache.addImage(tilesetInfo.sourceImage);
395 
396         if (this.initWithTexture(texture, capacity)) {
397             // layerInfo
398             this.layerName = layerInfo.name;
399             this._layerSize = size;
400             this.tiles = layerInfo._tiles;
401             this._minGID = layerInfo._minGID;
402             this._maxGID = layerInfo._maxGID;
403             this._opacity = layerInfo._opacity;
404             this.properties = layerInfo.properties;
405             this._contentScaleFactor = cc.director.getContentScaleFactor();
406 
407             // tilesetInfo
408             this.tileset = tilesetInfo;
409 
410             // mapInfo
411             this._mapTileSize = mapInfo.getTileSize();
412             this.layerOrientation = mapInfo.orientation;
413 
414             // offset (after layer orientation is set);
415             var offset = this._calculateLayerOffset(layerInfo.offset);
416             this.setPosition(cc.POINT_PIXELS_TO_POINTS(offset));
417 
418             this._atlasIndexArray = [];
419             this.setContentSize(cc.SIZE_PIXELS_TO_POINTS(cc.size(this._layerSize.width * this._mapTileSize.width,
420                 this._layerSize.height * this._mapTileSize.height)));
421             this._useAutomaticVertexZ = false;
422             this._vertexZvalue = 0;
423             return true;
424         }
425         return false;
426     },
427 
428     /**
429      * <p>Dealloc the map that contains the tile position from memory. <br />
430      * Unless you want to know at runtime the tiles positions, you can safely call this method. <br />
431      * If you are going to call layer.getTileGIDAt() then, don't release the map</p>
432      */
433     releaseMap:function () {
434         if (this.tiles)
435             this.tiles = null;
436 
437         if (this._atlasIndexArray)
438             this._atlasIndexArray = null;
439     },
440 
441     /**
442      * <p>Returns the tile (cc.Sprite) at a given a tile coordinate. <br/>
443      * The returned cc.Sprite will be already added to the cc.TMXLayer. Don't add it again.<br/>
444      * The cc.Sprite can be treated like any other cc.Sprite: rotated, scaled, translated, opacity, color, etc. <br/>
445      * You can remove either by calling: <br/>
446      * - layer.removeChild(sprite, cleanup); <br/>
447      * - or layer.removeTileAt(ccp(x,y)); </p>
448      * @param {cc.Point} pos
449      * @return {cc.Sprite}
450      */
451     getTileAt: function (pos) {
452         if(!pos)
453             throw "cc.TMXLayer.getTileAt(): pos should be non-null";
454         if(pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0)
455             throw "cc.TMXLayer.getTileAt(): invalid position";
456         if(!this.tiles || !this._atlasIndexArray){
457             cc.log("cc.TMXLayer.getTileAt(): TMXLayer: the tiles map has been released");
458             return null;
459         }
460 
461         var tile = null;
462         var gid = this.getTileGIDAt(pos);
463 
464         // if GID == 0, then no tile is present
465         if (gid === 0)
466             return tile;
467 
468         var z = 0 | (pos.x + pos.y * this._layerSize.width);
469         tile = this.getChildByTag(z);
470         // tile not created yet. create it
471         if (!tile) {
472             var rect = this.tileset.rectForGID(gid);
473             rect = cc.RECT_PIXELS_TO_POINTS(rect);
474 
475             tile = new cc.Sprite();
476             tile.initWithTexture(this.texture, rect);
477             tile.batchNode = this;
478             tile.setPosition(this.getPositionAt(pos));
479             tile.vertexZ = this._vertexZForPos(pos);
480             tile.anchorX = 0;
481 	        tile.anchorY = 0;
482             tile.opacity = this._opacity;
483 
484             var indexForZ = this._atlasIndexForExistantZ(z);
485             this.addSpriteWithoutQuad(tile, indexForZ, z);
486         }
487         return tile;
488     },
489 
490     /**
491      * Returns the tile gid at a given tile coordinate. <br />
492      * if it returns 0, it means that the tile is empty. <br />
493      * This method requires the the tile map has not been previously released (eg. don't call layer.releaseMap())<br />
494      * @param {cc.Point} pos
495      * @return {Number}
496      */
497     getTileGIDAt:function (pos) {
498         if(!pos)
499             throw "cc.TMXLayer.getTileGIDAt(): pos should be non-null";
500         if(pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0)
501             throw "cc.TMXLayer.getTileGIDAt(): invalid position";
502         if(!this.tiles || !this._atlasIndexArray){
503             cc.log("cc.TMXLayer.getTileGIDAt(): TMXLayer: the tiles map has been released");
504             return null;
505         }
506 
507         var idx = 0 | (pos.x + pos.y * this._layerSize.width);
508         // Bits on the far end of the 32-bit global tile ID are used for tile flags
509         var tile = this.tiles[idx];
510 
511         return (tile & cc.TMX_TILE_FLIPPED_MASK) >>> 0;
512     },
513     // XXX: deprecated
514     // tileGIDAt:getTileGIDAt,
515 
516     /**
517      *  lipped tiles can be changed dynamically
518      * @param {cc.Point} pos
519      * @return {Number}
520      */
521     getTileFlagsAt:function (pos) {
522         if(!pos)
523             throw "cc.TMXLayer.getTileFlagsAt(): pos should be non-null";
524         if(pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0)
525             throw "cc.TMXLayer.getTileFlagsAt(): invalid position";
526         if(!this.tiles || !this._atlasIndexArray){
527             cc.log("cc.TMXLayer.getTileFlagsAt(): TMXLayer: the tiles map has been released");
528             return null;
529         }
530 
531         var idx = 0 | (pos.x + pos.y * this._layerSize.width);
532         // Bits on the far end of the 32-bit global tile ID are used for tile flags
533         var tile = this.tiles[idx];
534 
535         return (tile & cc.TMX_TILE_FLIPPED_ALL) >>> 0;
536     },
537     // XXX: deprecated
538     // tileFlagAt:getTileFlagsAt,
539 
540     /**
541      * <p>Sets the tile gid (gid = tile global id) at a given tile coordinate.<br />
542      * The Tile GID can be obtained by using the method "tileGIDAt" or by using the TMX editor . Tileset Mgr +1.<br />
543      * If a tile is already placed at that position, then it will be removed.</p>
544      * @param {Number} gid
545      * @param {cc.Point} pos
546      * @param {Number} flags
547      */
548     setTileGID:function (gid, pos, flags) {
549         if(!pos)
550             throw "cc.TMXLayer.setTileGID(): pos should be non-null";
551         if(pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0)
552             throw "cc.TMXLayer.setTileGID(): invalid position";
553         if(!this.tiles || !this._atlasIndexArray){
554             cc.log("cc.TMXLayer.setTileGID(): TMXLayer: the tiles map has been released");
555             return null;
556         }
557         if(gid !== 0 && gid < this.tileset.firstGid){
558             cc.log( "cc.TMXLayer.setTileGID(): invalid gid:" + gid);
559             return null;
560         }
561 
562         flags = flags || 0;
563         this._setNodeDirtyForCache();
564 
565         var currentFlags = this.getTileFlagsAt(pos);
566         var currentGID = this.getTileGIDAt(pos);
567 
568         if (currentGID != gid || currentFlags != flags) {
569             var gidAndFlags = (gid | flags) >>> 0;
570             // setting gid=0 is equal to remove the tile
571             if (gid === 0)
572                 this.removeTileAt(pos);
573             else if (currentGID === 0)            // empty tile. create a new one
574                 this._insertTileForGID(gidAndFlags, pos);
575             else {                // modifying an existing tile with a non-empty tile
576                 var z = pos.x + pos.y * this._layerSize.width;
577                 var sprite = this.getChildByTag(z);
578                 if (sprite) {
579                     var rect = this.tileset.rectForGID(gid);
580                     rect = cc.RECT_PIXELS_TO_POINTS(rect);
581 
582                     sprite.setTextureRect(rect, false);
583                     if (flags != null)
584                         this._setupTileSprite(sprite, pos, gidAndFlags);
585 
586                     this.tiles[z] = gidAndFlags;
587                 } else
588                     this._updateTileForGID(gidAndFlags, pos);
589             }
590         }
591     },
592 
593     /**
594      * Removes a tile at given tile coordinate
595      * @param {cc.Point} pos
596      */
597     removeTileAt:function (pos) {
598         if(!pos)
599             throw "cc.TMXLayer.removeTileAt(): pos should be non-null";
600         if(pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0)
601             throw "cc.TMXLayer.removeTileAt(): invalid position";
602         if(!this.tiles || !this._atlasIndexArray){
603             cc.log("cc.TMXLayer.removeTileAt(): TMXLayer: the tiles map has been released");
604             return null;
605         }
606 
607         var gid = this.getTileGIDAt(pos);
608         if (gid !== 0) {
609             if (cc._renderType === cc._RENDER_TYPE_CANVAS)
610                 this._setNodeDirtyForCache();
611             var z = 0 | (pos.x + pos.y * this._layerSize.width);
612             var atlasIndex = this._atlasIndexForExistantZ(z);
613             // remove tile from GID map
614             this.tiles[z] = 0;
615 
616             // remove tile from atlas position array
617             this._atlasIndexArray.splice(atlasIndex, 1);
618 
619             // remove it from sprites and/or texture atlas
620             var sprite = this.getChildByTag(z);
621 
622             if (sprite)
623                 cc.SpriteBatchNode.prototype.removeChild.call(this, sprite, true);           //this.removeChild(sprite, true);
624             else {
625                 if(cc._renderType === cc._RENDER_TYPE_WEBGL)
626                     this.textureAtlas.removeQuadAtIndex(atlasIndex);
627 
628                 // update possible children
629                 if (this._children) {
630                     var locChildren = this._children;
631                     for (var i = 0, len = locChildren.length; i < len; i++) {
632                         var child = locChildren[i];
633                         if (child) {
634                             var ai = child.atlasIndex;
635                             if (ai >= atlasIndex)
636                                 child.atlasIndex = ai - 1;
637                         }
638                     }
639                 }
640             }
641         }
642     },
643 
644     /**
645      * Returns the position in pixels of a given tile coordinate
646      * @param {cc.Point} pos
647      * @return {cc.Point}
648      */
649     getPositionAt:function (pos) {
650         var ret = cc.p(0,0);
651         switch (this.layerOrientation) {
652             case cc.TMX_ORIENTATION_ORTHO:
653                 ret = this._positionForOrthoAt(pos);
654                 break;
655             case cc.TMX_ORIENTATION_ISO:
656                 ret = this._positionForIsoAt(pos);
657                 break;
658             case cc.TMX_ORIENTATION_HEX:
659                 ret = this._positionForHexAt(pos);
660                 break;
661         }
662         return cc.POINT_PIXELS_TO_POINTS(ret);
663     },
664     // XXX: Deprecated. For backward compatibility only
665     // positionAt:getPositionAt,
666 
667     /**
668      * Return the value for the specific property name
669      * @param {String} propertyName
670      * @return {*}
671      */
672     getProperty:function (propertyName) {
673         return this.properties[propertyName];
674     },
675 
676     /**
677      * Creates the tiles
678      */
679     setupTiles:function () {
680         // Optimization: quick hack that sets the image size on the tileset
681         if (cc._renderType === cc._RENDER_TYPE_CANVAS) {
682             this.tileset.imageSize = this._originalTexture.getContentSizeInPixels();
683         } else {
684             this.tileset.imageSize = this.textureAtlas.texture.getContentSizeInPixels();
685 
686             // By default all the tiles are aliased
687             // pros:
688             //  - easier to render
689             // cons:
690             //  - difficult to scale / rotate / etc.
691             this.textureAtlas.texture.setAliasTexParameters();
692         }
693 
694         // Parse cocos2d properties
695         this._parseInternalProperties();
696         if (cc._renderType === cc._RENDER_TYPE_CANVAS)
697             this._setNodeDirtyForCache();
698 
699         var locLayerHeight = this._layerSize.height, locLayerWidth = this._layerSize.width;
700         for (var y = 0; y < locLayerHeight; y++) {
701             for (var x = 0; x < locLayerWidth; x++) {
702                 var pos = x + locLayerWidth * y;
703                 var gid = this.tiles[pos];
704 
705                 // XXX: gid == 0 -. empty tile
706                 if (gid !== 0) {
707                     this._appendTileForGID(gid, cc.p(x, y));
708                     // Optimization: update min and max GID rendered by the layer
709                     this._minGID = Math.min(gid, this._minGID);
710                     this._maxGID = Math.max(gid, this._maxGID);
711                 }
712             }
713         }
714 
715         if (!((this._maxGID >= this.tileset.firstGid) && (this._minGID >= this.tileset.firstGid))) {
716             cc.log("cocos2d:TMX: Only 1 tileset per layer is supported");
717         }
718     },
719 
720     /**
721      * cc.TMXLayer doesn't support adding a cc.Sprite manually.
722      * @warning addChild(child); is not supported on cc.TMXLayer. Instead of setTileGID.
723      * @param {cc.Node} child
724      * @param {number} zOrder
725      * @param {number} tag
726      */
727     addChild:function (child, zOrder, tag) {
728         cc.log("addChild: is not supported on cc.TMXLayer. Instead use setTileGID or tileAt.");
729     },
730 
731     /**
732      * Remove child
733      * @param  {cc.Sprite} sprite
734      * @param  {Boolean} cleanup
735      */
736     removeChild:function (sprite, cleanup) {
737         // allows removing nil objects
738         if (!sprite)
739             return;
740 
741         if(this._children.indexOf(sprite) === -1){
742             cc.log("cc.TMXLayer.removeChild(): Tile does not belong to TMXLayer");
743             return;
744         }
745 
746         if (cc._renderType === cc._RENDER_TYPE_CANVAS)
747             this._setNodeDirtyForCache();
748         var atlasIndex = sprite.atlasIndex;
749         var zz = this._atlasIndexArray[atlasIndex];
750         this.tiles[zz] = 0;
751         this._atlasIndexArray.splice(atlasIndex, 1);
752         cc.SpriteBatchNode.prototype.removeChild.call(this, sprite, cleanup);
753     },
754 
755     /**
756      * @return {String}
757      */
758     getLayerName:function () {
759         return this.layerName;
760     },
761 
762     /**
763      * @param {String} layerName
764      */
765     setLayerName:function (layerName) {
766         this.layerName = layerName;
767     },
768 
769     _positionForIsoAt:function (pos) {
770         return cc.p(this._mapTileSize.width / 2 * ( this._layerSize.width + pos.x - pos.y - 1),
771             this._mapTileSize.height / 2 * (( this._layerSize.height * 2 - pos.x - pos.y) - 2));
772     },
773 
774     _positionForOrthoAt:function (pos) {
775         return cc.p(pos.x * this._mapTileSize.width,
776             (this._layerSize.height - pos.y - 1) * this._mapTileSize.height);
777     },
778 
779     _positionForHexAt:function (pos) {
780         var diffY = (pos.x % 2 == 1) ? (-this._mapTileSize.height / 2) : 0;
781         return cc.p(pos.x * this._mapTileSize.width * 3 / 4,
782             (this._layerSize.height - pos.y - 1) * this._mapTileSize.height + diffY);
783     },
784 
785     _calculateLayerOffset:function (pos) {
786         var ret = cc.p(0,0);
787         switch (this.layerOrientation) {
788             case cc.TMX_ORIENTATION_ORTHO:
789                 ret = cc.p(pos.x * this._mapTileSize.width, -pos.y * this._mapTileSize.height);
790                 break;
791             case cc.TMX_ORIENTATION_ISO:
792                 ret = cc.p((this._mapTileSize.width / 2) * (pos.x - pos.y),
793                     (this._mapTileSize.height / 2 ) * (-pos.x - pos.y));
794                 break;
795             case cc.TMX_ORIENTATION_HEX:
796                 if(pos.x !== 0 || pos.y !== 0)
797                     cc.log("offset for hexagonal map not implemented yet");
798                 break;
799         }
800         return ret;
801     },
802 
803     _appendTileForGID:function (gid, pos) {
804         var rect = this.tileset.rectForGID(gid);
805         rect = cc.RECT_PIXELS_TO_POINTS(rect);
806 
807         var z = 0 | (pos.x + pos.y * this._layerSize.width);
808         var tile = this._reusedTileWithRect(rect);
809         this._setupTileSprite(tile, pos, gid);
810 
811         // optimization:
812         // The difference between appendTileForGID and insertTileforGID is that append is faster, since
813         // it appends the tile at the end of the texture atlas
814         var indexForZ = this._atlasIndexArray.length;
815 
816         // don't add it using the "standard" way.
817         this.insertQuadFromSprite(tile, indexForZ);
818 
819         // append should be after addQuadFromSprite since it modifies the quantity values
820         this._atlasIndexArray.splice(indexForZ, 0, z);
821         return tile;
822     },
823 
824     _insertTileForGID:function (gid, pos) {
825         var rect = this.tileset.rectForGID(gid);
826         rect = cc.RECT_PIXELS_TO_POINTS(rect);
827 
828         var z = 0 | (pos.x + pos.y * this._layerSize.width);
829         var tile = this._reusedTileWithRect(rect);
830         this._setupTileSprite(tile, pos, gid);
831 
832         // get atlas index
833         var indexForZ = this._atlasIndexForNewZ(z);
834 
835         // Optimization: add the quad without adding a child
836         this.insertQuadFromSprite(tile, indexForZ);
837 
838         // insert it into the local atlasindex array
839         this._atlasIndexArray.splice(indexForZ, 0, z);
840         // update possible children
841         if (this._children) {
842             var locChildren = this._children;
843             for (var i = 0, len = locChildren.length; i < len; i++) {
844                 var child = locChildren[i];
845                 if (child) {
846                     var ai = child.atlasIndex;
847                     if (ai >= indexForZ)
848                         child.atlasIndex = ai + 1;
849                 }
850             }
851         }
852         this.tiles[z] = gid;
853         return tile;
854     },
855 
856     _updateTileForGID:function (gid, pos) {
857         var rect = this.tileset.rectForGID(gid);
858         var locScaleFactor = this._contentScaleFactor;
859         rect = cc.rect(rect.x / locScaleFactor, rect.y / locScaleFactor,
860             rect.width / locScaleFactor, rect.height / locScaleFactor);
861         var z = pos.x + pos.y * this._layerSize.width;
862 
863         var tile = this._reusedTileWithRect(rect);
864         this._setupTileSprite(tile, pos, gid);
865 
866         // get atlas index
867         var indexForZ = this._atlasIndexForExistantZ(z);
868         tile.atlasIndex = indexForZ;
869         tile.dirty = true;
870         tile.updateTransform();
871         this.tiles[z] = gid;
872 
873         return tile;
874     },
875 
876     //The layer recognizes some special properties, like cc_vertez
877     _parseInternalProperties:function () {
878         // if cc_vertex=automatic, then tiles will be rendered using vertexz
879         var vertexz = this.getProperty("cc_vertexz");
880         if (vertexz) {
881             if (vertexz == "automatic") {
882                 this._useAutomaticVertexZ = true;
883                 var alphaFuncVal = this.getProperty("cc_alpha_func");
884                 var alphaFuncValue = 0;
885                 if (alphaFuncVal)
886                     alphaFuncValue = parseFloat(alphaFuncVal);
887 
888                 if (cc._renderType === cc._RENDER_TYPE_WEBGL) {
889                     this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST);
890                     var alphaValueLocation = cc._renderContext.getUniformLocation(this.shaderProgram.getProgram(), cc.UNIFORM_ALPHA_TEST_VALUE_S);
891                     // NOTE: alpha test shader is hard-coded to use the equivalent of a glAlphaFunc(GL_GREATER) comparison
892                     this.shaderProgram.use();
893                     this.shaderProgram.setUniformLocationWith1f(alphaValueLocation, alphaFuncValue);
894                 }
895             } else
896                 this._vertexZvalue = parseInt(vertexz, 10);
897         }
898     },
899 
900     _setupTileSprite:function (sprite, pos, gid) {
901         var z = pos.x + pos.y * this._layerSize.width;
902         sprite.setPosition(this.getPositionAt(pos));
903         if (cc._renderType === cc._RENDER_TYPE_WEBGL)
904             sprite.vertexZ = this._vertexZForPos(pos);
905         else
906             sprite.tag = z;
907 
908         sprite.anchorX = 0;
909 	    sprite.anchorY = 0;
910         sprite.opacity = this._opacity;
911         if (cc._renderType === cc._RENDER_TYPE_WEBGL) {
912             sprite.rotation = 0.0;
913         }
914 
915         sprite.setFlippedX(false);
916         sprite.setFlippedY(false);
917 
918         // Rotation in tiled is achieved using 3 flipped states, flipping across the horizontal, vertical, and diagonal axes of the tiles.
919         if ((gid & cc.TMX_TILE_DIAGONAL_FLAG) >>> 0) {
920             // put the anchor in the middle for ease of rotation.
921             sprite.anchorX = 0.5;
922 	        sprite.anchorY = 0.5;
923             sprite.x = this.getPositionAt(pos).x + sprite.width / 2;
924 	        sprite.y = this.getPositionAt(pos).y + sprite.height / 2;
925 
926             var flag = (gid & (cc.TMX_TILE_HORIZONTAL_FLAG | cc.TMX_TILE_VERTICAL_FLAG) >>> 0) >>> 0;
927             // handle the 4 diagonally flipped states.
928             if (flag == cc.TMX_TILE_HORIZONTAL_FLAG)
929                 sprite.rotation = 90;
930             else if (flag == cc.TMX_TILE_VERTICAL_FLAG)
931                 sprite.rotation = 270;
932             else if (flag == (cc.TMX_TILE_VERTICAL_FLAG | cc.TMX_TILE_HORIZONTAL_FLAG) >>> 0) {
933                 sprite.rotation = 90;
934 	            sprite.setFlippedX(true);
935             } else {
936                 sprite.rotation = 270;
937 	            sprite.setFlippedX(true);
938             }
939         } else {
940             if ((gid & cc.TMX_TILE_HORIZONTAL_FLAG) >>> 0) {
941                 sprite.setFlippedX(true);
942             }
943 
944             if ((gid & cc.TMX_TILE_VERTICAL_FLAG) >>> 0) {
945                 sprite.setFlippedY(true);
946             }
947         }
948     },
949 
950     _reusedTileWithRect:function (rect) {
951         if(cc._renderType === cc._RENDER_TYPE_WEBGL){
952             if (!this._reusedTile) {
953                 this._reusedTile = new cc.Sprite();
954                 this._reusedTile.initWithTexture(this.texture, rect, false);
955                 this._reusedTile.batchNode = this;
956             } else {
957                 // XXX HACK: Needed because if "batch node" is nil,
958                 // then the Sprite'squad will be reset
959                 this._reusedTile.batchNode = null;
960 
961                 // Re-init the sprite
962                 this._reusedTile.setTextureRect(rect, false);
963 
964                 // restore the batch node
965                 this._reusedTile.batchNode = this;
966             }
967         } else {
968             this._reusedTile = new cc.Sprite();
969             this._reusedTile.initWithTexture(this._textureForCanvas, rect, false);
970             this._reusedTile.batchNode = this;
971             this._reusedTile.parent = this;
972         }
973         return this._reusedTile;
974     },
975 
976     _vertexZForPos:function (pos) {
977         var ret = 0;
978         var maxVal = 0;
979         if (this._useAutomaticVertexZ) {
980             switch (this.layerOrientation) {
981                 case cc.TMX_ORIENTATION_ISO:
982                     maxVal = this._layerSize.width + this._layerSize.height;
983                     ret = -(maxVal - (pos.x + pos.y));
984                     break;
985                 case cc.TMX_ORIENTATION_ORTHO:
986                     ret = -(this._layerSize.height - pos.y);
987                     break;
988                 case cc.TMX_ORIENTATION_HEX:
989                     cc.log("TMX Hexa zOrder not supported");
990                     break;
991                 default:
992                     cc.log("TMX invalid value");
993                     break;
994             }
995         } else
996             ret = this._vertexZvalue;
997         return ret;
998     },
999 
1000     _atlasIndexForExistantZ:function (z) {
1001         var item;
1002         if (this._atlasIndexArray) {
1003             var locAtlasIndexArray = this._atlasIndexArray;
1004             for (var i = 0, len = locAtlasIndexArray.length; i < len; i++) {
1005                 item = locAtlasIndexArray[i];
1006                 if (item == z)
1007                     break;
1008             }
1009         }
1010         if(typeof item != "number")
1011             cc.log("cc.TMXLayer._atlasIndexForExistantZ(): TMX atlas index not found. Shall not happen");
1012         return i;
1013     },
1014 
1015     _atlasIndexForNewZ:function (z) {
1016         var locAtlasIndexArray = this._atlasIndexArray;
1017         for (var i = 0, len = locAtlasIndexArray.length; i < len; i++) {
1018             var val = locAtlasIndexArray[i];
1019             if (z < val)
1020                 break;
1021         }
1022         return i;
1023     }
1024 });
1025 
1026 window._p = cc.TMXLayer.prototype;
1027 
1028 if(cc._renderType == cc._RENDER_TYPE_WEBGL){
1029 	_p.draw = cc.SpriteBatchNode.prototype.draw;
1030     _p.visit = cc.SpriteBatchNode.prototype.visit;
1031 	_p.getTexture = cc.SpriteBatchNode.prototype.getTexture;
1032 }else{
1033     _p.draw = _p._drawForCanvas;
1034     _p.visit = _p._visitForCanvas;
1035 	_p.getTexture = _p._getTextureForCanvas;
1036 }
1037 
1038 /** @expose */
1039 cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture);
1040 
1041 // Extended properties
1042 /** @expose */
1043 _p.layerWidth;
1044 cc.defineGetterSetter(_p, "layerWidth", _p._getLayerWidth, _p._setLayerWidth);
1045 /** @expose */
1046 _p.layerHeight;
1047 cc.defineGetterSetter(_p, "layerHeight", _p._getLayerHeight, _p._setLayerHeight);
1048 /** @expose */
1049 _p.tileWidth;
1050 cc.defineGetterSetter(_p, "tileWidth", _p._getTileWidth, _p._setTileWidth);
1051 /** @expose */
1052 _p.tileHeight;
1053 cc.defineGetterSetter(_p, "tileHeight", _p._getTileHeight, _p._setTileHeight);
1054 
1055 delete window._p;
1056 
1057 /**
1058  * Creates a cc.TMXLayer with an tile set info, a layer info and a map info
1059  * @param {cc.TMXTilesetInfo} tilesetInfo
1060  * @param {cc.TMXLayerInfo} layerInfo
1061  * @param {cc.TMXMapInfo} mapInfo
1062  * @return {cc.TMXLayer|Null}
1063  */
1064 cc.TMXLayer.create = function (tilesetInfo, layerInfo, mapInfo) {
1065     var ret = new cc.TMXLayer();
1066     if (ret.initWithTilesetInfo(tilesetInfo, layerInfo, mapInfo))
1067         return ret;
1068     return null;
1069 };
1070