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 cc.TMXLayer = cc.SpriteBatchNode.extend(/** @lends cc.TMXLayer# */{
 49     //size of the layer in tiles
 50     _layerSize: null,
 51     _mapTileSize: null,
 52     _tiles: null,
 53     _tileSet: null,
 54     _layerOrientation: null,
 55     _properties: null,
 56     //name of the layer
 57     _layerName: "",
 58     //TMX Layer supports opacity
 59     _opacity: 255,
 60     _minGID: null,
 61     _maxGID: null,
 62     //Only used when vertexZ is used
 63     _vertexZvalue: null,
 64     _useAutomaticVertexZ: null,
 65     _alphaFuncValue: null,
 66     //used for optimization
 67     _reusedTile: null,
 68     _atlasIndexArray: null,
 69     //used for retina display
 70     _contentScaleFactor: null,
 71 
 72     _cacheCanvas:null,
 73     _cacheContext:null,
 74     _cacheTexture:null,
 75 
 76     /**
 77      *  Constructor
 78      */
 79     ctor:function () {
 80         cc.SpriteBatchNode.prototype.ctor.call(this);
 81         this._children = [];
 82         this._descendants = [];
 83 
 84         this._layerSize = cc.SizeZero();
 85         this._mapTileSize = cc.SizeZero();
 86 
 87         if(cc.renderContextType === cc.CANVAS){
 88             var locCanvas = cc.canvas;
 89             var tmpCanvas = document.createElement('canvas');
 90             tmpCanvas.width = locCanvas.width;
 91             tmpCanvas.height = locCanvas.height;
 92             this._cacheCanvas = tmpCanvas;
 93             this._cacheContext = this._cacheCanvas.getContext('2d');
 94             var tempTexture = new cc.Texture2D();
 95             tempTexture.initWithElement(tmpCanvas);
 96             tempTexture.handleLoadedTexture();
 97             this._cacheTexture = tempTexture;
 98             this.setContentSize(cc.size(locCanvas.width, locCanvas.height));
 99         }
100     },
101 
102     setContentSize:function (size) {
103         if (!size)
104             return;
105         cc.Node.prototype.setContentSize.call(this, size);
106 
107         if(cc.renderContextType === cc.CANVAS){
108             var locCanvas = this._cacheCanvas;
109             locCanvas.width = 0|(size.width * 1.5);
110             locCanvas.height = 0|(size.height * 1.5);
111             this._cacheContext.translate(0, locCanvas.height);
112             var locContentSize = this._cacheTexture._contentSize;
113             locContentSize.width = locCanvas.width;
114             locContentSize.height = locCanvas.height;
115         }
116     },
117 
118     /**
119      * Return texture of cc.SpriteBatchNode
120      * @return {cc.Texture2D}
121      */
122     getTexture:function () {
123         if(cc.renderContextType === cc.CANVAS)
124             return this._cacheTexture;
125         else
126             return cc.SpriteBatchNode.prototype.getTexture.call(this);
127     },
128 
129     /**
130      * don't call visit on it's children ( override visit of cc.Node )
131      * @override
132      * @param {CanvasRenderingContext2D} ctx
133      */
134     visit:function (ctx) {
135         if (cc.renderContextType === cc.WEBGL) {
136             cc.SpriteBatchNode.prototype.visit.call(this, ctx);
137             return;
138         }
139         var context = ctx || cc.renderContext;
140         // quick return if not visible
141         if (!this._visible)
142             return;
143 
144         context.save();
145         this.transform(ctx);
146         var i, locChildren = this._children;
147 
148         if (this._cacheDirty) {
149             //add dirty region
150             var locCacheContext = this._cacheContext, locCacheCanvas = this._cacheCanvas;
151             locCacheContext.clearRect(0, 0, locCacheCanvas.width, -locCacheCanvas.height);
152             locCacheContext.save();
153             locCacheContext.translate(this._anchorPointInPoints.x, -(this._anchorPointInPoints.y ));
154             if (locChildren) {
155                 this.sortAllChildren();
156                 for (i = 0; i < locChildren.length; i++) {
157                     if (locChildren[i])
158                         locChildren[i].visit(locCacheContext);
159                 }
160             }
161             locCacheContext.restore();
162             this._cacheDirty = false;
163         }
164         // draw RenderTexture
165         this.draw(ctx);
166         context.restore();
167     },
168 
169     /**
170      * draw cc.SpriteBatchNode (override draw of cc.Node)
171      * @param {CanvasRenderingContext2D} ctx
172      */
173     draw:null,
174 
175     _drawForCanvas:function (ctx) {
176         var context = ctx || cc.renderContext;
177         //context.globalAlpha = this._opacity / 255;
178         var posX = 0 | ( -this._anchorPointInPoints.x), posY = 0 | ( -this._anchorPointInPoints.y);
179         var locCacheCanvas = this._cacheCanvas;
180         //direct draw image by canvas drawImage
181         if (locCacheCanvas)
182             context.drawImage(locCacheCanvas, posX, -(posY + locCacheCanvas.height));
183     },
184 
185     /**
186      * @return {cc.Size}
187      */
188     getLayerSize:function () {
189         return this._layerSize;
190     },
191 
192     /**
193      * @param {cc.Size} Var
194      */
195     setLayerSize:function (Var) {
196         this._layerSize = Var;
197     },
198 
199     /**
200      * Size of the map's tile (could be different from the tile's size)
201      * @return {cc.Size}
202      */
203     getMapTileSize:function () {
204         return this._mapTileSize;
205     },
206 
207     /**
208      * @param {cc.Size} Var
209      */
210     setMapTileSize:function (Var) {
211         this._mapTileSize = Var;
212     },
213 
214     /**
215      * Pointer to the map of tiles
216      * @return {Array}
217      */
218     getTiles:function () {
219         return this._tiles;
220     },
221 
222     /**
223      * @param {Array} Var
224      */
225     setTiles:function (Var) {
226         this._tiles = Var;
227     },
228 
229     /**
230      * Tile set information for the layer
231      * @return {cc.TMXTilesetInfo}
232      */
233     getTileSet:function () {
234         return this._tileSet;
235     },
236 
237     /**
238      * @param {cc.TMXTilesetInfo} Var
239      */
240     setTileSet:function (Var) {
241         this._tileSet = Var;
242     },
243 
244     /**
245      * Layer orientation, which is the same as the map orientation
246      * @return {Number}
247      */
248     getLayerOrientation:function () {
249         return this._layerOrientation;
250     },
251 
252     /**
253      * @param {Number} Var
254      */
255     setLayerOrientation:function (Var) {
256         this._layerOrientation = Var;
257     },
258 
259     /**
260      * properties from the layer. They can be added using Tiled
261      * @return {Array}
262      */
263     getProperties:function () {
264         return this._properties;
265     },
266 
267     /**
268      * @param {Array} Var
269      */
270     setProperties:function (Var) {
271         this._properties = Var;
272     },
273 
274     /**
275      * Initializes a cc.TMXLayer with a tileset info, a layer info and a map info
276      * @param {cc.TMXTilesetInfo} tilesetInfo
277      * @param {cc.TMXLayerInfo} layerInfo
278      * @param {cc.TMXMapInfo} mapInfo
279      * @return {Boolean}
280      */
281     initWithTilesetInfo:function (tilesetInfo, layerInfo, mapInfo) {
282         // XXX: is 35% a good estimate ?
283         var size = layerInfo._layerSize;
284         var totalNumberOfTiles = parseInt(size.width * size.height);
285         var capacity = totalNumberOfTiles * 0.35 + 1; // 35 percent is occupied ?
286         var texture;
287         if (tilesetInfo)
288             texture = cc.TextureCache.getInstance().addImage(tilesetInfo.sourceImage);
289 
290         if (this.initWithTexture(texture, capacity)) {
291             // layerInfo
292             this._layerName = layerInfo.name;
293             this._layerSize = size;
294             this._tiles = layerInfo._tiles;
295             this._minGID = layerInfo._minGID;
296             this._maxGID = layerInfo._maxGID;
297             this._opacity = layerInfo._opacity;
298             this.setProperties(layerInfo.getProperties());
299             this._contentScaleFactor = cc.Director.getInstance().getContentScaleFactor();
300 
301             // tilesetInfo
302             this._tileSet = tilesetInfo;
303 
304             // mapInfo
305             this._mapTileSize = mapInfo.getTileSize();
306             this._layerOrientation = mapInfo.getOrientation();
307 
308             // offset (after layer orientation is set);
309             var offset = this._calculateLayerOffset(layerInfo.offset);
310             this.setPosition(cc.POINT_PIXELS_TO_POINTS(offset));
311 
312             this._atlasIndexArray = [];
313             this.setContentSize(cc.SIZE_PIXELS_TO_POINTS(cc.size(this._layerSize.width * this._mapTileSize.width,
314                 this._layerSize.height * this._mapTileSize.height)));
315             this._useAutomaticVertexZ = false;
316             this._vertexZvalue = 0;
317             return true;
318         }
319         return false;
320     },
321 
322     /**
323      * <p>Dealloc the map that contains the tile position from memory. <br />
324      * Unless you want to know at runtime the tiles positions, you can safely call this method. <br />
325      * If you are going to call layer.getTileGIDAt() then, don't release the map</p>
326      */
327     releaseMap:function () {
328         if (this._tiles)
329             this._tiles = null;
330 
331         if (this._atlasIndexArray)
332             this._atlasIndexArray = null;
333     },
334 
335     /**
336      * <p>Returns the tile (cc.Sprite) at a given a tile coordinate. <br/>
337      * The returned cc.Sprite will be already added to the cc.TMXLayer. Don't add it again.<br/>
338      * The cc.Sprite can be treated like any other cc.Sprite: rotated, scaled, translated, opacity, color, etc. <br/>
339      * You can remove either by calling: <br/>
340      * - layer.removeChild(sprite, cleanup); <br/>
341      * - or layer.removeTileAt(ccp(x,y)); </p>
342      * @param {cc.Point} pos
343      * @return {cc.Sprite}
344      */
345     getTileAt: function (pos) {
346         cc.Assert(pos.x < this._layerSize.width && pos.y < this._layerSize.height && pos.x >= 0 && pos.y >= 0, "TMXLayer: invalid position");
347         cc.Assert(this._tiles && this._atlasIndexArray, "TMXLayer: the tiles map has been released");
348 
349         var tile = null;
350         var gid = this.getTileGIDAt(pos);
351 
352         // if GID == 0, then no tile is present
353         if (gid === 0)
354             return tile;
355 
356         var z = 0 | (pos.x + pos.y * this._layerSize.width);
357         tile = this.getChildByTag(z);
358         // tile not created yet. create it
359         if (!tile) {
360             var rect = this._tileSet.rectForGID(gid);
361             rect = cc.RECT_PIXELS_TO_POINTS(rect);
362 
363             tile = new cc.Sprite();
364             tile.initWithTexture(this.getTexture(), rect);
365             tile.setBatchNode(this);
366             tile.setPosition(this.getPositionAt(pos));
367             tile.setVertexZ(this._vertexZForPos(pos));
368             tile.setAnchorPoint(cc.PointZero());
369             tile.setOpacity(this._opacity);
370 
371             var indexForZ = this._atlasIndexForExistantZ(z);
372             this.addSpriteWithoutQuad(tile, indexForZ, z);
373         }
374         return tile;
375     },
376 
377     /**
378      * Returns the tile gid at a given tile coordinate. <br />
379      * if it returns 0, it means that the tile is empty. <br />
380      * This method requires the the tile map has not been previously released (eg. don't call layer.releaseMap())<br />
381      * @param {cc.Point} pos
382      * @return {Number}
383      */
384     getTileGIDAt:function (pos) {
385         cc.Assert(pos.x < this._layerSize.width && pos.y < this._layerSize.height && pos.x >= 0 && pos.y >= 0, "TMXLayer: invalid position");
386         cc.Assert(this._tiles && this._atlasIndexArray, "TMXLayer: the tiles map has been released");
387 
388         var idx = 0 | (pos.x + pos.y * this._layerSize.width);
389         // Bits on the far end of the 32-bit global tile ID are used for tile flags
390         var tile = this._tiles[idx];
391 
392         return (tile & cc.TMX_TILE_FLIPPED_MASK) >>> 0;
393     },
394     // XXX: deprecated
395     // tileGIDAt:getTileGIDAt,
396 
397     /**
398      *  lipped tiles can be changed dynamically
399      * @param {cc.Point} pos
400      * @return {Number}
401      */
402     getTileFlagsAt:function (pos) {
403         cc.Assert(pos.x < this._layerSize.width && pos.y < this._layerSize.height && pos.x >= 0 && pos.y >= 0, "TMXLayer: invalid position");
404         cc.Assert(this._tiles && this._atlasIndexArray, "TMXLayer: the tiles map has been released");
405 
406         var idx = 0 | (pos.x + pos.y * this._layerSize.width);
407         // Bits on the far end of the 32-bit global tile ID are used for tile flags
408         var tile = this._tiles[idx];
409 
410         return (tile & cc.TMX_TILE_FLIPPED_ALL) >>> 0;
411     },
412     // XXX: deprecated
413     // tileFlagAt:getTileFlagsAt,
414 
415     /**
416      * <p>Sets the tile gid (gid = tile global id) at a given tile coordinate.<br />
417      * The Tile GID can be obtained by using the method "tileGIDAt" or by using the TMX editor . Tileset Mgr +1.<br />
418      * If a tile is already placed at that position, then it will be removed.</p>
419      * @param {Number} gid
420      * @param {cc.Point} pos
421      * @param {Number} flags
422      */
423     setTileGID:function (gid, pos, flags) {
424         cc.Assert(pos.x < this._layerSize.width && pos.y < this._layerSize.height && pos.x >= 0 && pos.y >= 0, "TMXLayer: invalid position");
425         cc.Assert(this._tiles && this._atlasIndexArray, "TMXLayer: the tiles map has been released");
426         cc.Assert(gid !== 0 || !(gid >= this._tileSet.firstGid), "TMXLayer: invalid gid:" + gid);
427 
428         flags = flags || 0;
429         this._setNodeDirtyForCache();
430 
431         var currentFlags = this.getTileFlagsAt(pos);
432         var currentGID = this.getTileGIDAt(pos);
433 
434         if (currentGID != gid || currentFlags != flags) {
435             var gidAndFlags = (gid | flags) >>> 0;
436             // setting gid=0 is equal to remove the tile
437             if (gid === 0)
438                 this.removeTileAt(pos);
439             else if (currentGID === 0)            // empty tile. create a new one
440                 this._insertTileForGID(gidAndFlags, pos);
441             else {                // modifying an existing tile with a non-empty tile
442                 var z = pos.x + pos.y * this._layerSize.width;
443                 var sprite = this.getChildByTag(z);
444                 if (sprite) {
445                     var rect = this._tileSet.rectForGID(gid);
446                     rect = cc.RECT_PIXELS_TO_POINTS(rect);
447 
448                     sprite.setTextureRect(rect, false, rect.size);
449                     if (flags != null)
450                         this._setupTileSprite(sprite, pos, gidAndFlags);
451 
452                     this._tiles[z] = gidAndFlags;
453                 } else
454                     this._updateTileForGID(gidAndFlags, pos);
455             }
456         }
457     },
458 
459     /**
460      * Removes a tile at given tile coordinate
461      * @param {cc.Point} pos
462      */
463     removeTileAt:function (pos) {
464         cc.Assert(pos.x < this._layerSize.width && pos.y < this._layerSize.height && pos.x >= 0 && pos.y >= 0, "TMXLayer: invalid position");
465         cc.Assert(this._tiles && this._atlasIndexArray, "TMXLayer: the tiles map has been released");
466 
467         var gid = this.getTileGIDAt(pos);
468         if (gid !== 0) {
469             if (cc.renderContextType === cc.CANVAS)
470                 this._setNodeDirtyForCache();
471             var z = 0 | (pos.x + pos.y * this._layerSize.width);
472             var atlasIndex = this._atlasIndexForExistantZ(z);
473             // remove tile from GID map
474             this._tiles[z] = 0;
475 
476             // remove tile from atlas position array
477             cc.ArrayRemoveObjectAtIndex(this._atlasIndexArray, atlasIndex);
478 
479             // remove it from sprites and/or texture atlas
480             var sprite = this.getChildByTag(z);
481 
482             if (sprite)
483                 cc.SpriteBatchNode.prototype.removeChild.call(this, sprite, true);           //this.removeChild(sprite, true);
484             else {
485                 if(cc.renderContextType === cc.WEBGL)
486                     this._textureAtlas.removeQuadAtIndex(atlasIndex);
487 
488                 // update possible children
489                 if (this._children) {
490                     var locChildren = this._children;
491                     for (var i = 0, len = locChildren.length; i < len; i++) {
492                         var child = locChildren[i];
493                         if (child) {
494                             var ai = child.getAtlasIndex();
495                             if (ai >= atlasIndex)
496                                 child.setAtlasIndex(ai - 1);
497                         }
498                     }
499                 }
500             }
501         }
502     },
503 
504     /**
505      * Returns the position in pixels of a given tile coordinate
506      * @param {cc.Point} pos
507      * @return {cc.Point}
508      */
509     getPositionAt:function (pos) {
510         var ret = cc.PointZero();
511         switch (this._layerOrientation) {
512             case cc.TMX_ORIENTATION_ORTHO:
513                 ret = this._positionForOrthoAt(pos);
514                 break;
515             case cc.TMX_ORIENTATION_ISO:
516                 ret = this._positionForIsoAt(pos);
517                 break;
518             case cc.TMX_ORIENTATION_HEX:
519                 ret = this._positionForHexAt(pos);
520                 break;
521         }
522         return cc.POINT_PIXELS_TO_POINTS(ret);
523     },
524     // XXX: Deprecated. For backward compatibility only
525     // positionAt:getPositionAt,
526 
527     /**
528      * Return the value for the specific property name
529      * @param {String} propertyName
530      * @return {*}
531      */
532     getProperty:function (propertyName) {
533         return this._properties[propertyName];
534     },
535 
536     /**
537      * Creates the tiles
538      */
539     setupTiles:function () {
540         // Optimization: quick hack that sets the image size on the tileset
541         if (cc.renderContextType === cc.CANVAS) {
542             this._tileSet.imageSize = this._originalTexture.getContentSizeInPixels();
543         } else {
544             this._tileSet.imageSize = this._textureAtlas.getTexture().getContentSizeInPixels();
545 
546             // By default all the tiles are aliased
547             // pros:
548             //  - easier to render
549             // cons:
550             //  - difficult to scale / rotate / etc.
551             this._textureAtlas.getTexture().setAliasTexParameters();
552         }
553 
554         // Parse cocos2d properties
555         this._parseInternalProperties();
556         if (cc.renderContextType === cc.CANVAS)
557             this._setNodeDirtyForCache();
558 
559         var locLayerHeight = this._layerSize.height, locLayerWidth = this._layerSize.width;
560         for (var y = 0; y < locLayerHeight; y++) {
561             for (var x = 0; x < locLayerWidth; x++) {
562                 var pos = x + locLayerWidth * y;
563                 var gid = this._tiles[pos];
564 
565                 // XXX: gid == 0 -. empty tile
566                 if (gid !== 0) {
567                     this._appendTileForGID(gid, cc.p(x, y));
568                     // Optimization: update min and max GID rendered by the layer
569                     this._minGID = Math.min(gid, this._minGID);
570                     this._maxGID = Math.max(gid, this._maxGID);
571                 }
572             }
573         }
574 
575         if (!((this._maxGID >= this._tileSet.firstGid) && (this._minGID >= this._tileSet.firstGid))) {
576             cc.log("cocos2d:TMX: Only 1 tileset per layer is supported");
577         }
578     },
579 
580     /**
581      * cc.TMXLayer doesn't support adding a cc.Sprite manually.
582      * @warning addChild(child); is not supported on cc.TMXLayer. Instead of setTileGID.
583      * @param {cc.Node} child
584      * @param {number} zOrder
585      * @param {number} tag
586      */
587     addChild:function (child, zOrder, tag) {
588         cc.Assert(0, "addChild: is not supported on cc.TMXLayer. Instead use setTileGID:at:/tileAt:");
589     },
590 
591     /**
592      * Remove child
593      * @param  {cc.Sprite} sprite
594      * @param  {Boolean} cleanup
595      */
596     removeChild:function (sprite, cleanup) {
597         // allows removing nil objects
598         if (!sprite)
599             return;
600 
601         cc.Assert(cc.ArrayContainsObject(this._children, sprite), "Tile does not belong to TMXLayer");
602 
603         if (cc.renderContextType === cc.CANVAS)
604             this._setNodeDirtyForCache();
605         var atlasIndex = sprite.getAtlasIndex();         //cc.ArrayGetIndexOfObject(this._children, sprite);
606         var zz = this._atlasIndexArray[atlasIndex];
607         this._tiles[zz] = 0;
608         cc.ArrayRemoveObjectAtIndex(this._atlasIndexArray, atlasIndex);
609         cc.SpriteBatchNode.prototype.removeChild.call(this, sprite, cleanup);
610     },
611 
612     /**
613      * @return {String}
614      */
615     getLayerName:function () {
616         return this._layerName.toString();
617     },
618 
619     /**
620      * @param {String} layerName
621      */
622     setLayerName:function (layerName) {
623         this._layerName = layerName;
624     },
625 
626     _positionForIsoAt:function (pos) {
627         return cc.p(this._mapTileSize.width / 2 * ( this._layerSize.width + pos.x - pos.y - 1),
628             this._mapTileSize.height / 2 * (( this._layerSize.height * 2 - pos.x - pos.y) - 2));
629     },
630 
631     _positionForOrthoAt:function (pos) {
632         return cc.p(pos.x * this._mapTileSize.width,
633             (this._layerSize.height - pos.y - 1) * this._mapTileSize.height);
634     },
635 
636     _positionForHexAt:function (pos) {
637         var diffY = (pos.x % 2 == 1) ? (-this._mapTileSize.height / 2) : 0;
638         return cc.p(pos.x * this._mapTileSize.width * 3 / 4,
639             (this._layerSize.height - pos.y - 1) * this._mapTileSize.height + diffY);
640     },
641 
642     _calculateLayerOffset:function (pos) {
643         var ret = cc.PointZero();
644         switch (this._layerOrientation) {
645             case cc.TMX_ORIENTATION_ORTHO:
646                 ret = cc.p(pos.x * this._mapTileSize.width, -pos.y * this._mapTileSize.height);
647                 break;
648             case cc.TMX_ORIENTATION_ISO:
649                 ret = cc.p((this._mapTileSize.width / 2) * (pos.x - pos.y),
650                     (this._mapTileSize.height / 2 ) * (-pos.x - pos.y));
651                 break;
652             case cc.TMX_ORIENTATION_HEX:
653                 cc.Assert((pos.x == 0 && pos.y == 0), "offset for hexagonal map not implemented yet");
654                 break;
655         }
656         return ret;
657     },
658 
659     _appendTileForGID:function (gid, pos) {
660         var rect = this._tileSet.rectForGID(gid);
661         rect = cc.RECT_PIXELS_TO_POINTS(rect);
662 
663         var z = 0 | (pos.x + pos.y * this._layerSize.width);
664         var tile = this._reusedTileWithRect(rect);
665         this._setupTileSprite(tile, pos, gid);
666 
667         // optimization:
668         // The difference between appendTileForGID and insertTileforGID is that append is faster, since
669         // it appends the tile at the end of the texture atlas
670         var indexForZ = this._atlasIndexArray.length;
671 
672         // don't add it using the "standard" way.
673         this.insertQuadFromSprite(tile, indexForZ);
674 
675         // append should be after addQuadFromSprite since it modifies the quantity values
676         this._atlasIndexArray = cc.ArrayAppendObjectToIndex(this._atlasIndexArray, z, indexForZ);
677         return tile;
678     },
679 
680     _insertTileForGID:function (gid, pos) {
681         var rect = this._tileSet.rectForGID(gid);
682         rect = cc.RECT_PIXELS_TO_POINTS(rect);
683 
684         var z = 0 | (pos.x + pos.y * this._layerSize.width);
685         var tile = this._reusedTileWithRect(rect);
686         this._setupTileSprite(tile, pos, gid);
687 
688         // get atlas index
689         var indexForZ = this._atlasIndexForNewZ(z);
690 
691         // Optimization: add the quad without adding a child
692         this.insertQuadFromSprite(tile, indexForZ);
693 
694         // insert it into the local atlasindex array
695         this._atlasIndexArray = cc.ArrayAppendObjectToIndex(this._atlasIndexArray, z, indexForZ);
696         // update possible children
697         if (this._children) {
698             var locChildren = this._children;
699             for (var i = 0, len = locChildren.length; i < len; i++) {
700                 var child = locChildren[i];
701                 if (child) {
702                     var ai = child.getAtlasIndex();
703                     if (ai >= indexForZ)
704                         child.setAtlasIndex(ai + 1);
705                 }
706             }
707         }
708         this._tiles[z] = gid;
709         return tile;
710     },
711 
712     _updateTileForGID:function (gid, pos) {
713         var rect = this._tileSet.rectForGID(gid);
714         var locScaleFactor = this._contentScaleFactor;
715         rect = cc.rect(rect.x / locScaleFactor, rect.y / locScaleFactor,
716             rect.width / locScaleFactor, rect.height / locScaleFactor);
717         var z = pos.x + pos.y * this._layerSize.width;
718 
719         var tile = this._reusedTileWithRect(rect);
720         this._setupTileSprite(tile, pos, gid);
721 
722         // get atlas index
723         var indexForZ = this._atlasIndexForExistantZ(z);
724         tile.setAtlasIndex(indexForZ);
725         tile.setDirty(true);
726         tile.updateTransform();
727         this._tiles[z] = gid;
728 
729         return tile;
730     },
731 
732     //The layer recognizes some special properties, like cc_vertez
733     _parseInternalProperties:function () {
734         // if cc_vertex=automatic, then tiles will be rendered using vertexz
735         var vertexz = this.getProperty("cc_vertexz");
736         if (vertexz) {
737             if (vertexz == "automatic") {
738                 this._useAutomaticVertexZ = true;
739                 var alphaFuncVal = this.getProperty("cc_alpha_func");
740                 var alphaFuncValue = 0;
741                 if (alphaFuncVal)
742                     alphaFuncValue = parseFloat(alphaFuncVal);
743 
744                 if (cc.renderContextType === cc.WEBGL) {
745                     this.setShaderProgram(cc.ShaderCache.getInstance().programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST));
746                     var alphaValueLocation = cc.renderContext.getUniformLocation(this.getShaderProgram().getProgram(), cc.UNIFORM_ALPHA_TEST_VALUE_S);
747                     // NOTE: alpha test shader is hard-coded to use the equivalent of a glAlphaFunc(GL_GREATER) comparison
748                     this.getShaderProgram().use();
749                     this.getShaderProgram().setUniformLocationWith1f(alphaValueLocation, alphaFuncValue);
750                 }
751             } else
752                 this._vertexZvalue = parseInt(vertexz, 10);
753         }
754     },
755 
756     _setupTileSprite:function (sprite, pos, gid) {
757         var z = pos.x + pos.y * this._layerSize.width;
758         sprite.setPosition(this.getPositionAt(pos));
759         if (cc.renderContextType === cc.WEBGL)
760             sprite.setVertexZ(this._vertexZForPos(pos));
761         else
762             sprite.setTag(z);
763 
764         sprite.setAnchorPoint(cc.PointZero());
765         sprite.setOpacity(this._opacity);
766         if (cc.renderContextType === cc.WEBGL) {
767             sprite.setRotation(0.0);
768         }
769 
770         sprite.setFlippedX(false);
771         sprite.setFlippedY(false);
772 
773         // Rotation in tiled is achieved using 3 flipped states, flipping across the horizontal, vertical, and diagonal axes of the tiles.
774         if ((gid & cc.TMX_TILE_DIAGONAL_FLAG) >>> 0) {
775             // put the anchor in the middle for ease of rotation.
776             sprite.setAnchorPoint(cc.p(0.5, 0.5));
777             sprite.setPosition(this.getPositionAt(pos).x + sprite.getContentSize().height / 2,
778                 this.getPositionAt(pos).y + sprite.getContentSize().width / 2);
779 
780             var flag = (gid & (cc.TMX_TILE_HORIZONTAL_FLAG | cc.TMX_TILE_VERTICAL_FLAG) >>> 0) >>> 0;
781             // handle the 4 diagonally flipped states.
782             if (flag == cc.TMX_TILE_HORIZONTAL_FLAG)
783                 sprite.setRotation(90);
784             else if (flag == cc.TMX_TILE_VERTICAL_FLAG)
785                 sprite.setRotation(270);
786             else if (flag == (cc.TMX_TILE_VERTICAL_FLAG | cc.TMX_TILE_HORIZONTAL_FLAG) >>> 0) {
787                 sprite.setRotation(90);
788                 sprite.setFlippedX(true);
789             } else {
790                 sprite.setRotation(270);
791                 sprite.setFlippedX(true);
792             }
793         } else {
794             if ((gid & cc.TMX_TILE_HORIZONTAL_FLAG) >>> 0)
795                 sprite.setFlippedX(true);
796 
797             if ((gid & cc.TMX_TILE_VERTICAL_FLAG) >>> 0)
798                 sprite.setFlippedY(true);
799         }
800     },
801 
802     _reusedTileWithRect:function (rect) {
803         if(cc.renderContextType === cc.WEBGL){
804             if (!this._reusedTile) {
805                 this._reusedTile = new cc.Sprite();
806                 this._reusedTile.initWithTexture(this.getTexture(), rect, false);
807                 this._reusedTile.setBatchNode(this);
808             } else {
809                 // XXX HACK: Needed because if "batch node" is nil,
810                 // then the Sprite'squad will be reset
811                 this._reusedTile.setBatchNode(null);
812 
813                 // Re-init the sprite
814                 this._reusedTile.setTextureRect(rect, false, rect.size);
815 
816                 // restore the batch node
817                 this._reusedTile.setBatchNode(this);
818             }
819         } else {
820             this._reusedTile = new cc.Sprite();
821             this._reusedTile.initWithTexture(this._textureForCanvas, rect, false);
822             this._reusedTile.setBatchNode(this);
823             this._reusedTile.setParent(this);
824         }
825         return this._reusedTile;
826     },
827 
828     _vertexZForPos:function (pos) {
829         var ret = 0;
830         var maxVal = 0;
831         if (this._useAutomaticVertexZ) {
832             switch (this._layerOrientation) {
833                 case cc.TMX_ORIENTATION_ISO:
834                     maxVal = this._layerSize.width + this._layerSize.height;
835                     ret = -(maxVal - (pos.x + pos.y));
836                     break;
837                 case cc.TMX_ORIENTATION_ORTHO:
838                     ret = -(this._layerSize.height - pos.y);
839                     break;
840                 case cc.TMX_ORIENTATION_HEX:
841                     cc.Assert(0, "TMX Hexa zOrder not supported");
842                     break;
843                 default:
844                     cc.Assert(0, "TMX invalid value");
845                     break;
846             }
847         } else
848             ret = this._vertexZvalue;
849         return ret;
850     },
851 
852     _atlasIndexForExistantZ:function (z) {
853         var item;
854         if (this._atlasIndexArray) {
855             var locAtlasIndexArray = this._atlasIndexArray;
856             for (var i = 0, len = locAtlasIndexArray.length; i < len; i++) {
857                 item = locAtlasIndexArray[i];
858                 if (item == z)
859                     break;
860             }
861         }
862         cc.Assert(item != null, "TMX atlas index not found. Shall not happen");
863         return i;
864     },
865 
866     _atlasIndexForNewZ:function (z) {
867         var locAtlasIndexArray = this._atlasIndexArray;
868         for (var i = 0, len = locAtlasIndexArray.length; i < len; i++) {
869             var val = locAtlasIndexArray[i];
870             if (z < val)
871                 break;
872         }
873         return i;
874     }
875 });
876 
877 if(cc.Browser.supportWebGL){
878     cc.TMXLayer.prototype.draw = cc.SpriteBatchNode.prototype.draw;
879 }else{
880     cc.TMXLayer.prototype.draw = cc.TMXLayer.prototype._drawForCanvas;
881 }
882 
883 /**
884  * Creates a cc.TMXLayer with an tile set info, a layer info and a map info
885  * @param {cc.TMXTilesetInfo} tilesetInfo
886  * @param {cc.TMXLayerInfo} layerInfo
887  * @param {cc.TMXMapInfo} mapInfo
888  * @return {cc.TMXLayer|Null}
889  */
890 cc.TMXLayer.create = function (tilesetInfo, layerInfo, mapInfo) {
891     var ret = new cc.TMXLayer();
892     if (ret.initWithTilesetInfo(tilesetInfo, layerInfo, mapInfo))
893         return ret;
894     return null;
895 };
896