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