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