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