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 // ideas taken from:
 28 //	 . The ocean spray in your face [Jeff Lander]
 29 //		http://www.double.co.nz/dust/col0798.pdf
 30 //	 . Building an Advanced Particle System [John van der Burg]
 31 //		http://www.gamasutra.com/features/20000623/vanderburg_01.htm
 32 //   . LOVE game engine
 33 //		http://love2d.org/
 34 //
 35 //
 36 // Radius mode support, from 71 squared
 37 //		http://particledesigner.71squared.com/
 38 //
 39 // IMPORTANT: Particle Designer is supported by cocos2d, but
 40 // 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d,
 41 //  cocos2d uses a another approach, but the results are almost identical.
 42 //
 43 
 44 /**
 45  * Shape Mode of Particle Draw
 46  * @constant
 47  * @type Number
 48  */
 49 cc.PARTICLE_SHAPE_MODE = 0;
 50 /**
 51  * Texture Mode of Particle Draw
 52  * @constant
 53  * @type Number
 54  */
 55 cc.PARTICLE_TEXTURE_MODE = 1;
 56 
 57 /**
 58  * Star Shape for ShapeMode of Particle
 59  * @constant
 60  * @type Number
 61  */
 62 cc.PARTICLE_STAR_SHAPE = 0;
 63 /**
 64  * Ball Shape for ShapeMode of Particle
 65  * @constant
 66  * @type Number
 67  */
 68 cc.PARTICLE_BALL_SHAPE = 1;
 69 
 70 /**
 71  * The Particle emitter lives forever
 72  * @constant
 73  * @type Number
 74  */
 75 cc.PARTICLE_DURATION_INFINITY = -1;
 76 
 77 /**
 78  * The starting size of the particle is equal to the ending size
 79  * @constant
 80  * @type Number
 81  */
 82 cc.PARTICLE_START_SIZE_EQUAL_TO_END_SIZE = -1;
 83 
 84 /**
 85  * The starting radius of the particle is equal to the ending radius
 86  * @constant
 87  * @type Number
 88  */
 89 cc.PARTICLE_START_RADIUS_EQUAL_TO_END_RADIUS = -1;
 90 
 91 /**
 92  * Gravity mode (A mode)
 93  * @constant
 94  * @type Number
 95  */
 96 cc.PARTICLE_MODE_GRAVITY = 0;
 97 
 98 /**
 99  * Radius mode (B mode)
100  * @constant
101  * @type Number
102  */
103 cc.PARTICLE_MODE_RADIUS = 1;
104 
105 // tCCPositionType
106 // possible types of particle positions
107 
108 /**
109  * Living particles are attached to the world and are unaffected by emitter repositioning.
110  * @constant
111  * @type Number
112  */
113 cc.PARTICLE_TYPE_FREE = 0;
114 
115 /**
116  * Living particles are attached to the world but will follow the emitter repositioning.<br/>
117  * Use case: Attach an emitter to an sprite, and you want that the emitter follows the sprite.
118  * @constant
119  * @type Number
120  */
121 cc.PARTICLE_TYPE_RELATIVE = 1;
122 
123 /**
124  * Living particles are attached to the emitter and are translated along with it.
125  * @constant
126  * @type Number
127  */
128 cc.PARTICLE_TYPE_GROUPED = 2;
129 
130 // backward compatible
131 cc.PARTICLE_TYPE_FREE = cc.PARTICLE_TYPE_FREE;
132 cc.PARTICLE_TYPE_GROUPED = cc.PARTICLE_TYPE_GROUPED;
133 
134 
135 /**
136  * Structure that contains the values of each particle
137  * @Class
138  * @Construct
139  * @param {cc.Point} pos Position of particle
140  * @param {cc.Point} startPos
141  * @param {cc.Color4F} color
142  * @param {cc.Color4F} deltaColor
143  * @param {cc.Size} size
144  * @param {cc.Size} deltaSize
145  * @param {Number} rotation
146  * @param {Number} deltaRotation
147  * @param {Number} timeToLive
148  * @param {cc.Particle.ModeA} modeA
149  * @param {cc.Particle.ModeA} modeB
150  */
151 cc.Particle = function (pos, startPos, color, deltaColor, size, deltaSize, rotation, deltaRotation, timeToLive, atlasIndex, modeA, modeB) {
152     this.pos = pos ? pos : cc.PointZero();
153     this.startPos = startPos ? startPos : cc.PointZero();
154     this.color = color ? color : new cc.Color4F(0, 0, 0, 1);
155     this.deltaColor = deltaColor ? deltaColor : new cc.Color4F(0, 0, 0, 1);
156     this.size = size || 0;
157     this.deltaSize = deltaSize || 0;
158     this.rotation = rotation || 0;
159     this.deltaRotation = deltaRotation || 0;
160     this.timeToLive = timeToLive || 0;
161     this.atlasIndex = atlasIndex || 0;
162     this.modeA = modeA ? modeA : new cc.Particle.ModeA();
163     this.modeB = modeB ? modeB : new cc.Particle.ModeB();
164     this.isChangeColor = false;
165     this.drawPos = cc.p(0, 0);
166 };
167 
168 /**
169  * Mode A: gravity, direction, radial accel, tangential accel
170  * @Class
171  * @Construct
172  * @param {cc.Point} dir direction of particle
173  * @param {Number} radialAccel
174  * @param {Number} tangentialAccel
175  */
176 cc.Particle.ModeA = function (dir, radialAccel, tangentialAccel) {
177     this.dir = dir ? dir : cc.PointZero();
178     this.radialAccel = radialAccel || 0;
179     this.tangentialAccel = tangentialAccel || 0;
180 };
181 
182 /**
183  * Mode B: radius mode
184  * @Class
185  * @Construct
186  * @param {Number} angle
187  * @param {Number} degreesPerSecond
188  * @param {Number} radius
189  * @param {Number} deltaRadius
190  */
191 cc.Particle.ModeB = function (angle, degreesPerSecond, radius, deltaRadius) {
192     this.angle = angle || 0;
193     this.degreesPerSecond = degreesPerSecond || 0;
194     this.radius = radius || 0;
195     this.deltaRadius = deltaRadius || 0;
196 };
197 
198 
199 /**
200  * <p>
201  *     Particle System base class. <br/>
202  *     Attributes of a Particle System:<br/>
203  *     - emmision rate of the particles<br/>
204  *     - Gravity Mode (Mode A): <br/>
205  *     - gravity <br/>
206  *     - direction <br/>
207  *     - speed +-  variance <br/>
208  *     - tangential acceleration +- variance<br/>
209  *     - radial acceleration +- variance<br/>
210  *     - Radius Mode (Mode B):      <br/>
211  *     - startRadius +- variance    <br/>
212  *     - endRadius +- variance      <br/>
213  *     - rotate +- variance         <br/>
214  *     - Properties common to all modes: <br/>
215  *     - life +- life variance      <br/>
216  *     - start spin +- variance     <br/>
217  *     - end spin +- variance       <br/>
218  *     - start size +- variance     <br/>
219  *     - end size +- variance       <br/>
220  *     - start color +- variance    <br/>
221  *     - end color +- variance      <br/>
222  *     - life +- variance           <br/>
223  *     - blending function          <br/>
224  *     - texture                    <br/>
225  *                                  <br/>
226  *     cocos2d also supports particles generated by Particle Designer (http://particledesigner.71squared.com/).<br/>
227  *     'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d,  <br/>
228  *     cocos2d uses a another approach, but the results are almost identical.<br/>
229  *     cocos2d supports all the variables used by Particle Designer plus a bit more:  <br/>
230  *     - spinning particles (supported when using CCParticleSystemQuad)       <br/>
231  *     - tangential acceleration (Gravity mode)                               <br/>
232  *     - radial acceleration (Gravity mode)                                   <br/>
233  *     - radius direction (Radius mode) (Particle Designer supports outwards to inwards direction only) <br/>
234  *     It is possible to customize any of the above mentioned properties in runtime. Example:   <br/>
235  * </p>
236  * @class
237  * @extends cc.Node
238  *
239  * @example
240  *  emitter.radialAccel = 15;
241  *  emitter.startSpin = 0;
242  */
243 cc.ParticleSystem = cc.Node.extend(/** @lends cc.ParticleSystem# */{
244     //***********variables*************
245     _plistFile:"",
246     //! time elapsed since the start of the system (in seconds)
247     _elapsed:0,
248 
249     _dontTint:false,
250 
251     // Different modes
252     //! Mode A:Gravity + Tangential Accel + Radial Accel
253     modeA:null,
254     //! Mode B: circular movement (gravity, radial accel and tangential accel don't are not used in this mode)
255     modeB:null,
256 
257     //private POINTZERO for ParticleSystem
258     _pointZeroForParticle:cc.p(0,0),
259 
260     //! Array of particles
261     _particles:null,
262 
263     //particle pool
264     _particlePool:null,
265 
266     // color modulate
267     //	BOOL colorModulate;
268 
269     //! How many particles can be emitted per second
270     _emitCounter:0,
271     //!  particle idx
272     _particleIdx:0,
273 
274     _batchNode:null,
275     /**
276      * return weak reference to the cc.SpriteBatchNode that renders the cc.Sprite
277      * @return {cc.ParticleBatchNode}
278      */
279     getBatchNode:function () {
280         return this._batchNode;
281     },
282 
283     /**
284      *  set weak reference to the cc.SpriteBatchNode that renders the cc.Sprite
285      * @param {cc.ParticleBatchNode} batchNode
286      */
287     setBatchNode:function (batchNode) {
288         if (this._batchNode != batchNode) {
289             this._batchNode = batchNode; //weak reference
290 
291             if (batchNode) {
292                 for (var i = 0; i < this._totalParticles; i++) {
293                     this._particles[i].atlasIndex = i;
294                 }
295             }
296         }
297     },
298 
299     _atlasIndex:0,
300     /**
301      * return index of system in batch node array
302      * @return {Number}
303      */
304     getAtlasIndex:function () {
305         return this._atlasIndex;
306     },
307 
308     /**
309      * set index of system in batch node array
310      * @param {Number} atlasIndex
311      */
312     setAtlasIndex:function (atlasIndex) {
313         this._atlasIndex = atlasIndex;
314     },
315 
316     //true if scaled or rotated
317     _transformSystemDirty:false,
318 
319     _allocatedParticles:0,
320 
321     //drawMode
322     _drawMode:cc.PARTICLE_SHAPE_MODE,
323 
324     /**
325      * Return DrawMode of ParticleSystem
326      * @return {Number}
327      */
328     getDrawMode:function () {
329         return this._drawMode;
330     },
331 
332     /**
333      * DrawMode of ParticleSystem setter
334      * @param {Number} drawMode
335      */
336     setDrawMode:function (drawMode) {
337         this._drawMode = drawMode;
338     },
339 
340     //shape type
341     _shapeType:cc.PARTICLE_BALL_SHAPE,
342 
343     /**
344      * Return ShapeType of ParticleSystem
345      * @return {Number}
346      */
347     getShapeType:function () {
348         return this._shapeType;
349     },
350 
351     /**
352      * ShapeType of ParticleSystem setter
353      * @param {Number} shapeType
354      */
355     setShapeType:function (shapeType) {
356         this._shapeType = shapeType;
357     },
358 
359     _isActive:false,
360     /**
361      * Return ParticleSystem is active
362      * @return {Boolean}
363      */
364     isActive:function () {
365         return this._isActive;
366     },
367 
368     _particleCount:0,
369 
370     /**
371      * Quantity of particles that are being simulated at the moment
372      * @return {Number}
373      */
374     getParticleCount:function () {
375         return this._particleCount;
376     },
377 
378     /**
379      * Quantity of particles setter
380      * @param {Number} particleCount
381      */
382     setParticleCount:function (particleCount) {
383         this._particleCount = particleCount;
384     },
385 
386     _duration:0,
387     /**
388      * How many seconds the emitter wil run. -1 means 'forever'
389      * @return {Number}
390      */
391     getDuration:function () {
392         return this._duration;
393     },
394 
395     /**
396      * set run seconds of the emitter
397      * @param {Number} duration
398      */
399     setDuration:function (duration) {
400         this._duration = duration;
401     },
402 
403     _sourcePosition:cc.PointZero(),
404     /**
405      * Return sourcePosition of the emitter
406      * @return {cc.Point}
407      */
408     getSourcePosition:function () {
409         return this._sourcePosition;
410     },
411 
412     /**
413      * sourcePosition of the emitter setter
414      * @param sourcePosition
415      */
416     setSourcePosition:function (sourcePosition) {
417         this._sourcePosition = sourcePosition;
418     },
419 
420     _posVar:cc.PointZero(),
421     /**
422      * Return Position variance of the emitter
423      * @return {cc.Point}
424      */
425     getPosVar:function () {
426         return this._posVar;
427     },
428 
429     /**
430      * Position variance of the emitter setter
431      * @param {cc.Point} posVar
432      */
433     setPosVar:function (posVar) {
434         this._posVar = posVar;
435     },
436 
437     _life:0,
438     /**
439      * Return life of each particle
440      * @return {Number}
441      */
442     getLife:function () {
443         return this._life;
444     },
445 
446     /**
447      * life of each particle setter
448      * @param {Number} life
449      */
450     setLife:function (life) {
451         this._life = life;
452     },
453 
454     _lifeVar:0,
455     /**
456      * Return life variance of each particle
457      * @return {Number}
458      */
459     getLifeVar:function () {
460         return this._lifeVar;
461     },
462 
463     /**
464      * life variance of each particle setter
465      * @param {Number} lifeVar
466      */
467     setLifeVar:function (lifeVar) {
468         this._lifeVar = lifeVar;
469     },
470 
471     _angle:0,
472     /**
473      * Return angle of each particle
474      * @return {Number}
475      */
476     getAngle:function () {
477         return this._angle;
478     },
479 
480     /**
481      * angle of each particle setter
482      * @param {Number} angle
483      */
484     setAngle:function (angle) {
485         this._angle = angle;
486     },
487 
488     _angleVar:0,
489     /**
490      * Return angle variance of each particle
491      * @return {Number}
492      */
493     getAngleVar:function () {
494         return this._angleVar;
495     },
496 
497     /**
498      * angle variance of each particle setter
499      * @param angleVar
500      */
501     setAngleVar:function (angleVar) {
502         this._angleVar = angleVar;
503     },
504 
505     // mode A
506     /**
507      * Return Gravity of emitter
508      * @return {cc.Point}
509      */
510     getGravity:function () {
511         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity");
512         return this.modeA.gravity;
513     },
514 
515     /**
516      * Gravity of emitter setter
517      * @param {cc.Point} gravity
518      */
519     setGravity:function (gravity) {
520         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity");
521         this.modeA.gravity = gravity;
522     },
523 
524     /**
525      * Return Speed of each particle
526      * @return {Number}
527      */
528     getSpeed:function () {
529         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity");
530         return this.modeA.speed;
531     },
532 
533     /**
534      * Speed of each particle setter
535      * @param {Number} speed
536      */
537     setSpeed:function (speed) {
538         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity");
539         this.modeA.speed = speed;
540     },
541 
542     /**
543      * return speed variance of each particle. Only available in 'Gravity' mode.
544      * @return {Number}
545      */
546     getSpeedVar:function () {
547         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity");
548         return this.modeA.speedVar;
549     },
550 
551     /**
552      * speed variance of each particle setter. Only available in 'Gravity' mode.
553      * @param {Number} speedVar
554      */
555     setSpeedVar:function (speedVar) {
556         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity");
557         this.modeA.speedVar = speedVar;
558     },
559 
560     /**
561      * Return tangential acceleration of each particle. Only available in 'Gravity' mode.
562      * @return {Number}
563      */
564     getTangentialAccel:function () {
565         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity");
566         return this.modeA.tangentialAccel;
567     },
568 
569     /**
570      * Tangential acceleration of each particle setter. Only available in 'Gravity' mode.
571      * @param {Number} tangentialAccel
572      */
573     setTangentialAccel:function (tangentialAccel) {
574         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity");
575         this.modeA.tangentialAccel = tangentialAccel;
576     },
577 
578     /**
579      * Return tangential acceleration variance of each particle. Only available in 'Gravity' mode.
580      * @return {Number}
581      */
582     getTangentialAccelVar:function () {
583         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity");
584         return this.modeA.tangentialAccelVar;
585     },
586 
587     /**
588      * tangential acceleration variance of each particle setter. Only available in 'Gravity' mode.
589      * @param {Number} tangentialAccelVar
590      */
591     setTangentialAccelVar:function (tangentialAccelVar) {
592         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity");
593         this.modeA.tangentialAccelVar = tangentialAccelVar;
594     },
595 
596     /**
597      * Return radial acceleration of each particle. Only available in 'Gravity' mode.
598      * @return {Number}
599      */
600     getRadialAccel:function () {
601         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity");
602         return this.modeA.radialAccel;
603     },
604 
605     /**
606      * radial acceleration of each particle setter. Only available in 'Gravity' mode.
607      * @param {Number} radialAccel
608      */
609     setRadialAccel:function (radialAccel) {
610         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity");
611         this.modeA.radialAccel = radialAccel;
612     },
613 
614     /**
615      * Return radial acceleration variance of each particle. Only available in 'Gravity' mode.
616      * @return {Number}
617      */
618     getRadialAccelVar:function () {
619         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity");
620         return this.modeA.radialAccelVar;
621     },
622 
623     /**
624      * radial acceleration variance of each particle setter. Only available in 'Gravity' mode.
625      * @param radialAccelVar
626      */
627     setRadialAccelVar:function (radialAccelVar) {
628         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity");
629         this.modeA.radialAccelVar = radialAccelVar;
630     },
631 
632     // mode B
633     /**
634      * Return starting radius of the particles. Only available in 'Radius' mode.
635      * @return {Number}
636      */
637     getStartRadius:function () {
638         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius");
639         return this.modeB.startRadius;
640     },
641 
642     /**
643      * starting radius of the particles setter. Only available in 'Radius' mode.
644      * @param {Number} startRadius
645      */
646     setStartRadius:function (startRadius) {
647         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius");
648         this.modeB.startRadius = startRadius;
649     },
650 
651     /**
652      * Return starting radius variance of the particles. Only available in 'Radius' mode.
653      * @return {Number}
654      */
655     getStartRadiusVar:function () {
656         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius");
657         return this.modeB.startRadiusVar;
658     },
659 
660     /**
661      * starting radius variance of the particles setter. Only available in 'Radius' mode.
662      * @param {Number} startRadiusVar
663      */
664     setStartRadiusVar:function (startRadiusVar) {
665         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius");
666         this.modeB.startRadiusVar = startRadiusVar;
667     },
668 
669     /**
670      * Return ending radius of the particles. Only available in 'Radius' mode.
671      * @return {Number}
672      */
673     getEndRadius:function () {
674         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius");
675         return this.modeB.endRadius;
676     },
677 
678     /**
679      * ending radius of the particles setter. Only available in 'Radius' mode.
680      * @param {Number} endRadius
681      */
682     setEndRadius:function (endRadius) {
683         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius");
684         this.modeB.endRadius = endRadius;
685     },
686 
687     /**
688      * Return ending radius variance of the particles. Only available in 'Radius' mode.
689      * @return {Number}
690      */
691     getEndRadiusVar:function () {
692         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius");
693         return this.modeB.endRadiusVar;
694     },
695 
696     /**
697      * ending radius variance of the particles setter. Only available in 'Radius' mode.
698      * @param endRadiusVar
699      */
700     setEndRadiusVar:function (endRadiusVar) {
701         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius");
702         this.modeB.endRadiusVar = endRadiusVar;
703     },
704 
705     /**
706      * get Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode.
707      * @return {Number}
708      */
709     getRotatePerSecond:function () {
710         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius");
711         return this.modeB.rotatePerSecond;
712     },
713 
714     /**
715      * set Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode.
716      * @param {Number} degrees
717      */
718     setRotatePerSecond:function (degrees) {
719         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius");
720         this.modeB.rotatePerSecond = degrees;
721     },
722 
723     /**
724      * Return Variance in degrees for rotatePerSecond. Only available in 'Radius' mode.
725      * @return {Number}
726      */
727     getRotatePerSecondVar:function () {
728         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius");
729         return this.modeB.rotatePerSecondVar;
730     },
731 
732     /**
733      * Variance in degrees for rotatePerSecond setter. Only available in 'Radius' mode.
734      * @param degrees
735      */
736     setRotatePerSecondVar:function (degrees) {
737         cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius");
738         this.modeB.rotatePerSecondVar = degrees;
739     },
740     //////////////////////////////////////////////////////////////////////////
741 
742     //don't use a transform matrix, this is faster
743     setScale:function (scale, scaleY) {
744         this._transformSystemDirty = true;
745         this._super(scale, scaleY);
746     },
747 
748     setRotation:function (newRotation) {
749         this._transformSystemDirty = true;
750         this._super(newRotation);
751     },
752 
753     setScaleX:function (newScaleX) {
754         this._transformSystemDirty = true;
755         this._super(newScaleX);
756     },
757 
758     setScaleY:function (newScaleY) {
759         this._transformSystemDirty = true;
760         this._super(newScaleY);
761     },
762 
763 
764     _startSize:0,
765     /**
766      * get start size in pixels of each particle
767      * @return {Number}
768      */
769     getStartSize:function () {
770         return this._startSize;
771     },
772 
773     /**
774      * set start size in pixels of each particle
775      * @param {Number} startSize
776      */
777     setStartSize:function (startSize) {
778         this._startSize = startSize;
779     },
780 
781 
782     _startSizeVar:0,
783     /**
784      * get size variance in pixels of each particle
785      * @return {Number}
786      */
787     getStartSizeVar:function () {
788         return this._startSizeVar;
789     },
790 
791     /**
792      * set size variance in pixels of each particle
793      * @param {Number} startSizeVar
794      */
795     setStartSizeVar:function (startSizeVar) {
796         this._startSizeVar = startSizeVar;
797     },
798 
799 
800     _endSize:0,
801     /**
802      * get end size in pixels of each particle
803      * @return {Number}
804      */
805     getEndSize:function () {
806         return this._endSize;
807     },
808 
809     /**
810      * set end size in pixels of each particle
811      * @param endSize
812      */
813     setEndSize:function (endSize) {
814         this._endSize = endSize;
815     },
816 
817     _endSizeVar:0,
818     /**
819      * get end size variance in pixels of each particle
820      * @return {Number}
821      */
822     getEndSizeVar:function () {
823         return this._endSizeVar;
824     },
825 
826     /**
827      * set end size variance in pixels of each particle
828      * @param {Number} endSizeVar
829      */
830     setEndSizeVar:function (endSizeVar) {
831         this._endSizeVar = endSizeVar;
832     },
833 
834 
835     _startColor:new cc.Color4F(0, 0, 0, 1),
836     /**
837      * set start color of each particle
838      * @return {cc.Color4F}
839      */
840     getStartColor:function () {
841         return this._startColor;
842     },
843 
844     /**
845      * get start color of each particle
846      * @param {cc.Color4F} startColor
847      */
848     setStartColor:function (startColor) {
849         this._startColor = startColor;
850     },
851 
852     _startColorVar:new cc.Color4F(0, 0, 0, 1),
853     /**
854      * get start color variance of each particle
855      * @return {cc.Color4F}
856      */
857     getStartColorVar:function () {
858         return this._startColorVar;
859     },
860 
861     /**
862      * set start color variance of each particle
863      * @param {cc.Color4F} startColorVar
864      */
865     setStartColorVar:function (startColorVar) {
866         this._startColorVar = startColorVar;
867     },
868 
869 
870     _endColor:new cc.Color4F(0, 0, 0, 1),
871     /**
872      * get end color and end color variation of each particle
873      * @return {cc.Color4F}
874      */
875     getEndColor:function () {
876         return this._endColor;
877     },
878 
879     /**
880      * set end color and end color variation of each particle
881      * @param {cc.Color4F} endColor
882      */
883     setEndColor:function (endColor) {
884         this._endColor = endColor;
885     },
886 
887     _endColorVar:new cc.Color4F(0, 0, 0, 1),
888     /**
889      * get end color variance of each particle
890      * @return {cc.Color4F}
891      */
892     getEndColorVar:function () {
893         return this._endColorVar;
894     },
895 
896     /**
897      * set end color variance of each particle
898      * @param {cc.Color4F} endColorVar
899      */
900     setEndColorVar:function (endColorVar) {
901         this._endColorVar = endColorVar;
902     },
903 
904     _startSpin:0,
905     /**
906      * get initial angle of each particle
907      * @return {Number}
908      */
909     getStartSpin:function () {
910         return this._startSpin;
911     },
912 
913     /**
914      * set initial angle of each particle
915      * @param {Number} startSpin
916      */
917     setStartSpin:function (startSpin) {
918         this._startSpin = startSpin;
919     },
920 
921     _startSpinVar:0,
922     /**
923      * get initial angle variance of each particle
924      * @return {Number}
925      */
926     getStartSpinVar:function () {
927         return this._startSpinVar;
928     },
929 
930     /**
931      * set initial angle variance of each particle
932      * @param {Number} startSpinVar
933      */
934     setStartSpinVar:function (startSpinVar) {
935         this._startSpinVar = startSpinVar;
936     },
937 
938     _endSpin:0,
939     /**
940      * get end angle of each particle
941      * @return {Number}
942      */
943     getEndSpin:function () {
944         return this._endSpin;
945     },
946 
947     /**
948      * set end angle of each particle
949      * @param {Number} endSpin
950      */
951     setEndSpin:function (endSpin) {
952         this._endSpin = endSpin;
953     },
954 
955     _endSpinVar:0,
956     /**
957      * get end angle variance of each particle
958      * @return {Number}
959      */
960     getEndSpinVar:function () {
961         return this._endSpinVar;
962     },
963 
964     /**
965      * set end angle variance of each particle
966      * @param {Number} endSpinVar
967      */
968     setEndSpinVar:function (endSpinVar) {
969         this._endSpinVar = endSpinVar;
970     },
971 
972     _emissionRate:0,
973     /**
974      * get emission rate of the particles
975      * @return {Number}
976      */
977     getEmissionRate:function () {
978         return this._emissionRate;
979     },
980 
981     /**
982      * set emission rate of the particles
983      * @param {Number} emissionRate
984      */
985     setEmissionRate:function (emissionRate) {
986         this._emissionRate = emissionRate;
987     },
988 
989     _totalParticles:0,
990     /**
991      * get maximum particles of the system
992      * @return {Number}
993      */
994     getTotalParticles:function () {
995         return this._totalParticles;
996     },
997 
998     /**
999      * set maximum particles of the system
1000      * @param {Number} totalParticles
1001      */
1002     setTotalParticles:function (totalParticles) {
1003         cc.Assert(totalParticles <= this._allocatedParticles, "Particle: resizing particle array only supported for quads");
1004         this._totalParticles = totalParticles;
1005     },
1006 
1007     _texture:null,
1008     /**
1009      * get Texture of Particle System
1010      * @return {cc.Texture2D}
1011      */
1012     getTexture:function () {
1013         return this._texture;
1014     },
1015 
1016     /**
1017      * set Texture of Particle System
1018      * @param {cc.Texture2D | HTMLImageElement | HTMLCanvasElement} texture
1019      */
1020     setTexture:function (texture) {
1021         //TODO
1022         if (this._texture != texture) {
1023             this._texture = texture;
1024             this._updateBlendFunc();
1025         }
1026     },
1027 
1028     /** conforms to CocosNodeTexture protocol */
1029     _blendFunc: {src:gl.ONE, dst:gl.ONE},
1030     /**
1031      * get BlendFunc of Particle System
1032      * @return {cc.BlendFunc}
1033      */
1034     getBlendFunc:function () {
1035         return this._blendFunc;
1036     },
1037 
1038     /**
1039      * set BlendFunc of Particle System
1040      * @param {Number} src
1041      * @param {Number} dst
1042      */
1043     setBlendFunc:function (src, dst) {
1044         if(arguments.length == 1){
1045             if (this._blendFunc != src ) {
1046                 this._blendFunc = src;
1047                 this._updateBlendFunc();
1048             }
1049         }else{
1050             if (this._blendFunc.src != src || this._blendFunc.dst != dst) {
1051                 this._blendFunc = {src:src, dst:dst};
1052                 this._updateBlendFunc();
1053             }
1054         }
1055 
1056     },
1057 
1058     _opacityModifyRGB:false,
1059     /**
1060      * does the alpha value modify color getter
1061      * @return {Boolean}
1062      */
1063     getOpacityModifyRGB:function () {
1064         return this._opacityModifyRGB;
1065     },
1066 
1067     /**
1068      * does the alpha value modify color setter
1069      * @param newValue
1070      */
1071     setOpacityModifyRGB:function (newValue) {
1072         this._opacityModifyRGB = newValue;
1073     },
1074 
1075     _isBlendAdditive:false,
1076     /**
1077      * <p>whether or not the particles are using blend additive.<br/>
1078      *     If enabled, the following blending function will be used.<br/>
1079      * </p>
1080      * @return {Boolean}
1081      * @example
1082      *    source blend function = GL_SRC_ALPHA;
1083      *    dest blend function = GL_ONE;
1084      */
1085     isBlendAdditive:function () {
1086         //return this._isBlendAdditive;
1087         return (( this._blendFunc.src == gl.SRC_ALPHA && this._blendFunc.dst == gl.ONE) || (this._blendFunc.src == gl.ONE && this._blendFunc.dst == gl.ONE));
1088     },
1089 
1090     /**
1091      * <p>whether or not the particles are using blend additive.<br/>
1092      *     If enabled, the following blending function will be used.<br/>
1093      * </p>
1094      * @param {Boolean} isBlendAdditive
1095      */
1096     setBlendAdditive:function (isBlendAdditive) {
1097         //TODO
1098         this._isBlendAdditive = isBlendAdditive;
1099         if (isBlendAdditive) {
1100             this._blendFunc.src = gl.SRC_ALPHA;
1101             this._blendFunc.dst = gl.ONE;
1102         } else {
1103             this._blendFunc.src = cc.BLEND_SRC;
1104             this._blendFunc.dst = cc.BLEND_DST;
1105             /*if (this._texture && !this._texture.hasPremultipliedAlpha()) {
1106              this._blendFunc.src = gl.SRC_ALPHA;
1107              this._blendFunc.dst = gl.ONE_MINUS_SRC_ALPHA;
1108              } else {
1109              this._blendFunc.src = cc.BLEND_SRC;
1110              this._blendFunc.dst = cc.BLEND_DST;
1111              }*/
1112         }
1113     },
1114 
1115     _positionType:cc.PARTICLE_TYPE_FREE,
1116     /**
1117      * get particles movement type: Free or Grouped
1118      * @return {Number}
1119      */
1120     getPositionType:function () {
1121         return this._positionType;
1122     },
1123 
1124     /**
1125      * set particles movement type: Free or Grouped
1126      * @param {Number} positionType
1127      */
1128     setPositionType:function (positionType) {
1129         this._positionType = positionType;
1130     },
1131 
1132     _isAutoRemoveOnFinish:false,
1133     /**
1134      *  <p> return whether or not the node will be auto-removed when it has no particles left.<br/>
1135      *      By default it is false.<br/>
1136      *  </p>
1137      * @return {Boolean}
1138      */
1139     isAutoRemoveOnFinish:function () {
1140         return this._isAutoRemoveOnFinish;
1141     },
1142 
1143     /**
1144      *  <p> set whether or not the node will be auto-removed when it has no particles left.<br/>
1145      *      By default it is false.<br/>
1146      *  </p>
1147      * @param {Boolean} isAutoRemoveOnFinish
1148      */
1149     setAutoRemoveOnFinish:function (isAutoRemoveOnFinish) {
1150         this._isAutoRemoveOnFinish = isAutoRemoveOnFinish;
1151     },
1152 
1153     _emitterMode:0,
1154     /**
1155      * return kind of emitter modes
1156      * @return {Number}
1157      */
1158     getEmitterMode:function () {
1159         return this._emitterMode;
1160     },
1161 
1162     /**
1163      * <p>Switch between different kind of emitter modes:<br/>
1164      *  - CCPARTICLE_MODE_GRAVITY: uses gravity, speed, radial and tangential acceleration<br/>
1165      *  - CCPARTICLE_MODE_RADIUS: uses radius movement + rotation <br/>
1166      *  </p>
1167      * @param {Number} emitterMode
1168      */
1169     setEmitterMode:function (emitterMode) {
1170         this._emitterMode = emitterMode;
1171     },
1172 
1173     /**
1174      * Constructor
1175      * @override
1176      */
1177     ctor:function () {
1178         this._super();
1179         this._emitterMode = cc.PARTICLE_MODE_GRAVITY;
1180         this.modeA = new cc.ParticleSystem.ModeA();
1181         this.modeB = new cc.ParticleSystem.ModeB();
1182         this._blendFunc = {src:cc.BLEND_SRC, dst:cc.BLEND_DST};
1183 
1184         this._particles = [];
1185         this._sourcePosition = new cc.Point(0,0);
1186         this._posVar = new cc.Point(0,0);
1187 
1188         this._startColor = new cc.Color4F(1,1,1,1);
1189         this._startColorVar = new cc.Color4F(1,1,1,1);
1190         this._endColor = new cc.Color4F(1,1,1,1);
1191         this._endColorVar = new cc.Color4F(1,1,1,1);
1192 
1193         this._particlePool = [];
1194     },
1195 
1196     /**
1197      * initializes a cc.ParticleSystem
1198      */
1199     init:function () {
1200         return this.initWithTotalParticles(150);
1201     },
1202 
1203     /**
1204      * <p> initializes a CCParticleSystem from a plist file. <br/>
1205      *      This plist files can be creted manually or with Particle Designer:<br/>
1206      *      http://particledesigner.71squared.com/<br/></p>
1207      * @param {String} plistFile
1208      * @return {cc.ParticleSystem}
1209      */
1210     initWithFile:function (plistFile) {
1211         var ret = false;
1212         //TODO
1213         this._plistFile = plistFile;
1214         var dict = cc.FileUtils.getInstance().dictionaryWithContentsOfFileThreadSafe(this._plistFile);
1215 
1216         cc.Assert(dict != null, "Particles: file not found");
1217         return this.initWithDictionary(dict);
1218     },
1219 
1220     /**
1221      * return bounding box of particle system in world space
1222      * @return {cc.Rect}
1223      */
1224     getBoundingBoxToWorld:function () {
1225         return cc.rect(0, 0, cc.canvas.width, cc.canvas.height);
1226     },
1227 
1228     /**
1229      * initializes a CCQuadParticleSystem from a CCDictionary.
1230      * @param {object} dictionary
1231      * @return {Boolean}
1232      */
1233     initWithDictionary:function (dictionary) {
1234         var ret = false;
1235         var buffer = null;
1236         var deflated = null;
1237         var image = null;
1238 
1239         var maxParticles = parseInt(this._valueForKey("maxParticles", dictionary));
1240         // self, not super
1241         if (this.initWithTotalParticles(maxParticles)) {
1242             // angle
1243             this._angle = parseFloat(this._valueForKey("angle", dictionary));
1244             this._angleVar = parseFloat(this._valueForKey("angleVariance", dictionary));
1245 
1246             // duration
1247             this._duration = parseFloat(this._valueForKey("duration", dictionary));
1248 
1249             // blend function
1250             this._blendFunc.src = parseInt(this._valueForKey("blendFuncSource", dictionary));
1251             this._blendFunc.dst = parseInt(this._valueForKey("blendFuncDestination", dictionary));
1252 
1253             // color
1254             this._startColor.r = parseFloat(this._valueForKey("startColorRed", dictionary));
1255             this._startColor.g = parseFloat(this._valueForKey("startColorGreen", dictionary));
1256             this._startColor.b = parseFloat(this._valueForKey("startColorBlue", dictionary));
1257             this._startColor.a = parseFloat(this._valueForKey("startColorAlpha", dictionary));
1258 
1259             this._startColorVar.r = parseFloat(this._valueForKey("startColorVarianceRed", dictionary));
1260             this._startColorVar.g = parseFloat(this._valueForKey("startColorVarianceGreen", dictionary));
1261             this._startColorVar.b = parseFloat(this._valueForKey("startColorVarianceBlue", dictionary));
1262             this._startColorVar.a = parseFloat(this._valueForKey("startColorVarianceAlpha", dictionary));
1263 
1264             this._endColor.r = parseFloat(this._valueForKey("finishColorRed", dictionary));
1265             this._endColor.g = parseFloat(this._valueForKey("finishColorGreen", dictionary));
1266             this._endColor.b = parseFloat(this._valueForKey("finishColorBlue", dictionary));
1267             this._endColor.a = parseFloat(this._valueForKey("finishColorAlpha", dictionary));
1268 
1269             this._endColorVar.r = parseFloat(this._valueForKey("finishColorVarianceRed", dictionary));
1270             this._endColorVar.g = parseFloat(this._valueForKey("finishColorVarianceGreen", dictionary));
1271             this._endColorVar.b = parseFloat(this._valueForKey("finishColorVarianceBlue", dictionary));
1272             this._endColorVar.a = parseFloat(this._valueForKey("finishColorVarianceAlpha", dictionary));
1273 
1274             // particle size
1275             this._startSize = parseFloat(this._valueForKey("startParticleSize", dictionary));
1276             this._startSizeVar = parseFloat(this._valueForKey("startParticleSizeVariance", dictionary));
1277             this._endSize = parseFloat(this._valueForKey("finishParticleSize", dictionary));
1278             this._endSizeVar = parseFloat(this._valueForKey("finishParticleSizeVariance", dictionary));
1279 
1280             // position
1281             var x = parseFloat(this._valueForKey("sourcePositionx", dictionary));
1282             var y = parseFloat(this._valueForKey("sourcePositiony", dictionary));
1283             this.setPosition(cc.p(x, y));
1284             this._posVar.x = parseFloat(this._valueForKey("sourcePositionVariancex", dictionary));
1285             this._posVar.y = parseFloat(this._valueForKey("sourcePositionVariancey", dictionary));
1286 
1287             // Spinning
1288             this._startSpin = parseFloat(this._valueForKey("rotationStart", dictionary));
1289             this._startSpinVar = parseFloat(this._valueForKey("rotationStartVariance", dictionary));
1290             this._endSpin = parseFloat(this._valueForKey("rotationEnd", dictionary));
1291             this._endSpinVar = parseFloat(this._valueForKey("rotationEndVariance", dictionary));
1292 
1293             this._emitterMode = parseInt(this._valueForKey("emitterType", dictionary));
1294 
1295             // Mode A: Gravity + tangential accel + radial accel
1296             if (this._emitterMode == cc.PARTICLE_MODE_GRAVITY) {
1297                 // gravity
1298                 this.modeA.gravity.x = parseFloat(this._valueForKey("gravityx", dictionary));
1299                 this.modeA.gravity.y = parseFloat(this._valueForKey("gravityy", dictionary));
1300 
1301                 // speed
1302                 this.modeA.speed = parseFloat(this._valueForKey("speed", dictionary));
1303                 this.modeA.speedVar = parseFloat(this._valueForKey("speedVariance", dictionary));
1304 
1305                 // radial acceleration
1306                 var pszTmp = this._valueForKey("radialAcceleration", dictionary);
1307                 this.modeA.radialAccel = (pszTmp) ? parseFloat(pszTmp) : 0;
1308 
1309                 pszTmp = this._valueForKey("radialAccelVariance", dictionary);
1310                 this.modeA.radialAccelVar = (pszTmp) ? parseFloat(pszTmp) : 0;
1311 
1312                 // tangential acceleration
1313                 pszTmp = this._valueForKey("tangentialAcceleration", dictionary);
1314                 this.modeA.tangentialAccel = (pszTmp) ? parseFloat(pszTmp) : 0;
1315 
1316                 pszTmp = this._valueForKey("tangentialAccelVariance", dictionary);
1317                 this.modeA.tangentialAccelVar = (pszTmp) ? parseFloat(pszTmp) : 0;
1318 
1319             } else if (this._emitterMode == cc.PARTICLE_MODE_RADIUS) {
1320                 // or Mode B: radius movement
1321                 this.modeB.startRadius = parseFloat(this._valueForKey("maxRadius", dictionary));
1322                 this.modeB.startRadiusVar = parseFloat(this._valueForKey("maxRadiusVariance", dictionary));
1323                 this.modeB.endRadius = parseFloat(this._valueForKey("minRadius", dictionary));
1324                 this.modeB.endRadiusVar = 0;
1325                 this.modeB.rotatePerSecond = parseFloat(this._valueForKey("rotatePerSecond", dictionary));
1326                 this.modeB.rotatePerSecondVar = parseFloat(this._valueForKey("rotatePerSecondVariance", dictionary));
1327             } else {
1328                 cc.Assert(false, "Invalid emitterType in config file");
1329                 return false;
1330             }
1331 
1332             // life span
1333             this._life = parseFloat(this._valueForKey("particleLifespan", dictionary));
1334             this._lifeVar = parseFloat(this._valueForKey("particleLifespanVariance", dictionary));
1335 
1336             // emission Rate
1337             this._emissionRate = this._totalParticles / this._life;
1338 
1339             //don't get the internal texture if a batchNode is used
1340             if (!this._batchNode) {
1341                 // Set a compatible default for the alpha transfer
1342                 this._opacityModifyRGB = false;
1343 
1344                 // texture
1345                 // Try to get the texture from the cache
1346                 var textureName = this._valueForKey("textureFileName", dictionary);
1347                 var fullpath = cc.FileUtils.getInstance().fullPathFromRelativeFile(textureName, this._plistFile);
1348 
1349                 var tex = cc.TextureCache.getInstance().textureForKey(fullpath);
1350 
1351                 if (tex) {
1352                     this._texture = tex;
1353                 } else {
1354                     var textureData = this._valueForKey("textureImageData", dictionary);
1355 
1356                     if (textureData && textureData.length == 0) {
1357                         cc.Assert(textureData, "cc.ParticleSystem.initWithDictory:textureImageData is null");
1358                         tex = cc.TextureCache.getInstance().addImage(fullpath);
1359                         if (!tex)
1360                             return false;
1361                         this._texture = tex;
1362                     } else {
1363                         buffer = cc.unzipBase64AsArray(textureData, 1);
1364                         if (!buffer)
1365                             return false;
1366                         var newImageData = cc.encodeToBase64(buffer);
1367                         if (!newImageData)
1368                             return false;
1369 
1370                         var img = new Image();
1371                         img.src = "data:image/png;base64," + newImageData;
1372                         this._texture = img;
1373 
1374                         //save image to TextureCache
1375                         cc.TextureCache.getInstance().cacheImage(fullpath, img);
1376                     }
1377                 }
1378                 cc.Assert(this._texture != null, "cc.ParticleSystem: error loading the texture");
1379             }
1380             ret = true;
1381         }
1382         return ret;
1383     },
1384 
1385     /**
1386      * Initializes a system with a fixed number of particles
1387      * @param {Number} numberOfParticles
1388      * @return {Boolean}
1389      */
1390     initWithTotalParticles:function (numberOfParticles) {
1391         this._totalParticles = numberOfParticles;
1392 
1393         this._particles = [];
1394         this._particlePool = [];
1395 
1396         if (!this._particles) {
1397             cc.log("Particle system: not enough memory");
1398             return false;
1399         }
1400         this._allocatedParticles = numberOfParticles;
1401 
1402         if (this._batchNode)
1403             for (var i = 0; i < this._totalParticles; i++)
1404                 this._particles[i].atlasIndex = i;
1405 
1406         // default, active
1407         this._isActive = true;
1408 
1409         // default blend function
1410         this._blendFunc.src = cc.BLEND_SRC;
1411         this._blendFunc.dst = cc.BLEND_DST;
1412 
1413         // default movement type;
1414         this._positionType = cc.PARTICLE_TYPE_FREE;
1415 
1416         // by default be in mode A:
1417         this._emitterMode = cc.PARTICLE_MODE_GRAVITY;
1418 
1419         // default: modulate
1420         // XXX: not used
1421         //	colorModulate = YES;
1422         this._isAutoRemoveOnFinish = false;
1423 
1424         // Optimization: compile udpateParticle method
1425         //updateParticleSel = @selector(updateQuadWithParticle:newPosition:);
1426         //updateParticleImp = (CC_UPDATE_PARTICLE_IMP) [self methodForSelector:updateParticleSel];
1427 
1428         //for batchNode
1429         this._transformSystemDirty = false;
1430 
1431         // udpate after action in run!
1432         this.scheduleUpdateWithPriority(1);
1433 
1434         return true;
1435     },
1436 
1437     destroyParticleSystem:function () {
1438         this._particlePool = null;
1439         this.unscheduleUpdate();
1440     },
1441 
1442     _getParticleObject:function(){
1443         if(this._particlePool.length > 0)
1444             return this._particlePool.pop();
1445         return new cc.Particle();
1446     },
1447 
1448     /**
1449      * Add a particle to the emitter
1450      * @return {Boolean}
1451      */
1452     addParticle:function () {
1453         if (this.isFull())
1454             return false;
1455 
1456         var particle = this._getParticleObject();
1457         this.initParticle(particle);
1458         this._particles.push(particle);
1459         ++this._particleCount;
1460 
1461         return true;
1462     },
1463 
1464     /**
1465      * Initializes a particle
1466      * @param {cc.Particle} particle
1467      */
1468     initParticle:function (particle) {
1469         // timeToLive
1470         // no negative life. prevent division by 0
1471         particle.timeToLive = this._life + this._lifeVar * cc.RANDOM_MINUS1_1();
1472         particle.timeToLive = Math.max(0, particle.timeToLive);
1473 
1474         // position
1475         particle.pos.x = this._sourcePosition.x + this._posVar.x * cc.RANDOM_MINUS1_1();
1476         particle.pos.y = this._sourcePosition.y + this._posVar.y * cc.RANDOM_MINUS1_1();
1477 
1478         // Color
1479         var start = new cc.Color4F(
1480             cc.clampf(this._startColor.r + this._startColorVar.r * cc.RANDOM_MINUS1_1(), 0, 1),
1481             cc.clampf(this._startColor.g + this._startColorVar.g * cc.RANDOM_MINUS1_1(), 0, 1),
1482             cc.clampf(this._startColor.b + this._startColorVar.b * cc.RANDOM_MINUS1_1(), 0, 1),
1483             cc.clampf(this._startColor.a + this._startColorVar.a * cc.RANDOM_MINUS1_1(), 0, 1)
1484         );
1485 
1486         var end = new cc.Color4F(
1487             cc.clampf(this._endColor.r + this._endColorVar.r * cc.RANDOM_MINUS1_1(), 0, 1),
1488             cc.clampf(this._endColor.g + this._endColorVar.g * cc.RANDOM_MINUS1_1(), 0, 1),
1489             cc.clampf(this._endColor.b + this._endColorVar.b * cc.RANDOM_MINUS1_1(), 0, 1),
1490             cc.clampf(this._endColor.a + this._endColorVar.a * cc.RANDOM_MINUS1_1(), 0, 1)
1491         );
1492 
1493         particle.color = start;
1494         particle.deltaColor.r = (end.r - start.r) / particle.timeToLive;
1495         particle.deltaColor.g = (end.g - start.g) / particle.timeToLive;
1496         particle.deltaColor.b = (end.b - start.b) / particle.timeToLive;
1497         particle.deltaColor.a = (end.a - start.a) / particle.timeToLive;
1498 
1499         // size
1500         var startS = this._startSize + this._startSizeVar * cc.RANDOM_MINUS1_1();
1501         startS = Math.max(0, startS); // No negative value
1502 
1503         particle.size = startS;
1504 
1505         if (this._endSize == cc.PARTICLE_START_SIZE_EQUAL_TO_END_SIZE) {
1506             particle.deltaSize = 0;
1507         } else {
1508             var endS = this._endSize + this._endSizeVar * cc.RANDOM_MINUS1_1();
1509             endS = Math.max(0, endS); // No negative values
1510             particle.deltaSize = (endS - startS) / particle.timeToLive;
1511         }
1512 
1513         // rotation
1514         var startA = this._startSpin + this._startSpinVar * cc.RANDOM_MINUS1_1();
1515         var endA = this._endSpin + this._endSpinVar * cc.RANDOM_MINUS1_1();
1516         particle.rotation = startA;
1517         particle.deltaRotation = (endA - startA) / particle.timeToLive;
1518 
1519         // position
1520         if (this._positionType == cc.PARTICLE_TYPE_FREE)
1521             particle.startPos = this.convertToWorldSpace(this._pointZeroForParticle);
1522         else if (this._positionType == cc.PARTICLE_TYPE_RELATIVE)
1523             particle.startPos = this._position;
1524 
1525         // direction
1526         var a = cc.DEGREES_TO_RADIANS(this._angle + this._angleVar * cc.RANDOM_MINUS1_1());
1527 
1528         // Mode Gravity: A
1529         if (this._emitterMode == cc.PARTICLE_MODE_GRAVITY) {
1530             var v = cc.p(Math.cos(a), Math.sin(a));
1531             var s = this.modeA.speed + this.modeA.speedVar * cc.RANDOM_MINUS1_1();
1532 
1533             // direction
1534             particle.modeA.dir = cc.pMult(v, s);
1535 
1536             // radial accel
1537             particle.modeA.radialAccel = this.modeA.radialAccel + this.modeA.radialAccelVar * cc.RANDOM_MINUS1_1();
1538 
1539             // tangential accel
1540             particle.modeA.tangentialAccel = this.modeA.tangentialAccel + this.modeA.tangentialAccelVar * cc.RANDOM_MINUS1_1();
1541         } else {
1542             // Mode Radius: B
1543 
1544             // Set the default diameter of the particle from the source position
1545             var startRadius = this.modeB.startRadius + this.modeB.startRadiusVar * cc.RANDOM_MINUS1_1();
1546             var endRadius = this.modeB.endRadius + this.modeB.endRadiusVar * cc.RANDOM_MINUS1_1();
1547 
1548             particle.modeB.radius = startRadius;
1549 
1550             if (this.modeB.endRadius == cc.PARTICLE_START_RADIUS_EQUAL_TO_END_RADIUS) {
1551                 particle.modeB.deltaRadius = 0;
1552             } else {
1553                 particle.modeB.deltaRadius = (endRadius - startRadius) / particle.timeToLive;
1554             }
1555 
1556             particle.modeB.angle = a;
1557             particle.modeB.degreesPerSecond = cc.DEGREES_TO_RADIANS(this.modeB.rotatePerSecond + this.modeB.rotatePerSecondVar * cc.RANDOM_MINUS1_1());
1558         }
1559     },
1560 
1561     /**
1562      * stop emitting particles. Running particles will continue to run until they die
1563      */
1564     stopSystem:function () {
1565         this._isActive = false;
1566         this._elapsed = this._duration;
1567         this._emitCounter = 0;
1568 
1569         this._particlePool = [];
1570     },
1571 
1572     /**
1573      * Kill all living particles.
1574      */
1575     resetSystem:function () {
1576         this._isActive = true;
1577         this._elapsed = 0;
1578         for (this._particleIdx = 0; this._particleIdx < this._particleCount; ++this._particleIdx) {
1579             var p = this._particles[this._particleIdx];
1580             p.timeToLive = 0;
1581         }
1582     },
1583 
1584     /**
1585      * whether or not the system is full
1586      * @return {Boolean}
1587      */
1588     isFull:function () {
1589         return (this._particleCount >= this._totalParticles);
1590     },
1591 
1592     /**
1593      * should be overriden by subclasses
1594      * @param {cc.Particle} particle
1595      * @param {cc.Point} newPosition
1596      */
1597     updateQuadWithParticle:function (particle, newPosition) {
1598         // should be overriden
1599     },
1600 
1601     /**
1602      * should be overriden by subclasses
1603      */
1604     postStep:function () {
1605         // should be overriden
1606     },
1607 
1608     /**
1609      * update emitter's status
1610      * @override
1611      * @param {Number} dt delta time
1612      */
1613     update:function (dt) {
1614         if (this._isActive && this._emissionRate) {
1615             var rate = 1.0 / this._emissionRate;
1616             //issue #1201, prevent bursts of particles, due to too high emitCounter
1617             if (this._particleCount < this._totalParticles)
1618                 this._emitCounter += dt;
1619 
1620             while ((this._particleCount < this._totalParticles) && (this._emitCounter > rate)) {
1621                 this.addParticle();
1622                 this._emitCounter -= rate;
1623             }
1624 
1625             this._elapsed += dt;
1626             if (this._duration != -1 && this._duration < this._elapsed)
1627                 this.stopSystem();
1628         }
1629         this._particleIdx = 0;
1630 
1631         var currentPosition; // = cc.PointZero();
1632         if (this._positionType == cc.PARTICLE_TYPE_FREE) {
1633             currentPosition = this.convertToWorldSpace(this._pointZeroForParticle);
1634         } else if (this._positionType == cc.PARTICLE_TYPE_RELATIVE) {
1635             currentPosition = cc.p(this._position.x, this._position.y);
1636         }
1637 
1638         if (this._visible) {
1639             while (this._particleIdx < this._particleCount) {
1640                 var selParticle = this._particles[this._particleIdx];
1641 
1642                 // life
1643                 selParticle.timeToLive -= dt;
1644 
1645                 if (selParticle.timeToLive > 0) {
1646                     // Mode A: gravity, direction, tangential accel & radial accel
1647                     if (this._emitterMode == cc.PARTICLE_MODE_GRAVITY) {
1648                         var tmp, radial, tangential;
1649 
1650                         // radial acceleration
1651                         if (selParticle.pos.x || selParticle.pos.y)
1652                             radial = cc.pNormalize(selParticle.pos);
1653                         else
1654                             radial = cc.PointZero();
1655 
1656                         tangential = radial;
1657                         radial = cc.pMult(radial, selParticle.modeA.radialAccel);
1658 
1659                         // tangential acceleration
1660                         var newy = tangential.x;
1661                         tangential.x = -tangential.y;
1662                         tangential.y = newy;
1663                         tangential = cc.pMult(tangential, selParticle.modeA.tangentialAccel);
1664 
1665                         // (gravity + radial + tangential) * dt
1666                         tmp = cc.pAdd(cc.pAdd(radial, tangential), this.modeA.gravity);
1667                         tmp = cc.pMult(tmp, dt);
1668                         selParticle.modeA.dir = cc.pAdd(selParticle.modeA.dir, tmp);
1669                         tmp = cc.pMult(selParticle.modeA.dir, dt);
1670                         selParticle.pos = cc.pAdd(selParticle.pos, tmp);
1671                     } else {
1672                         // Mode B: radius movement
1673 
1674                         // Update the angle and radius of the particle.
1675                         selParticle.modeB.angle += selParticle.modeB.degreesPerSecond * dt;
1676                         selParticle.modeB.radius += selParticle.modeB.deltaRadius * dt;
1677 
1678                         selParticle.pos.x = -Math.cos(selParticle.modeB.angle) * selParticle.modeB.radius;
1679                         selParticle.pos.y = -Math.sin(selParticle.modeB.angle) * selParticle.modeB.radius;
1680                     }
1681 
1682                     // color
1683                     if (!this._dontTint) {
1684                         selParticle.color.r += (selParticle.deltaColor.r * dt);
1685                         selParticle.color.g += (selParticle.deltaColor.g * dt);
1686                         selParticle.color.b += (selParticle.deltaColor.b * dt);
1687                         selParticle.color.a += (selParticle.deltaColor.a * dt);
1688                         selParticle.isChangeColor = true;
1689                     }
1690 
1691                     // size
1692                     selParticle.size += (selParticle.deltaSize * dt);
1693                     selParticle.size = Math.max(0, selParticle.size);
1694 
1695                     // angle
1696                     selParticle.rotation += (selParticle.deltaRotation * dt);
1697 
1698                     //
1699                     // update values in quad
1700                     //
1701                     var newPos;
1702                     if (this._positionType == cc.PARTICLE_TYPE_FREE || this._positionType == cc.PARTICLE_TYPE_RELATIVE) {
1703                         var diff = cc.pSub(currentPosition, selParticle.startPos);
1704                         newPos = cc.pSub(selParticle.pos, diff);
1705                     } else {
1706                         newPos = selParticle.pos;
1707                     }
1708 
1709                     // translate newPos to correct position, since matrix transform isn't performed in batchnode
1710                     // don't update the particle with the new position information, it will interfere with the radius and tangential calculations
1711                     if (this._batchNode) {
1712                         newPos.x += this._position.x;
1713                         newPos.y += this._position.y;
1714                     }
1715 
1716                     if (cc.renderContextType == cc.WEBGL) {
1717                         this.updateQuadWithParticle(selParticle, newPos);
1718                     } else {
1719                         selParticle.drawPos = newPos;
1720                     }
1721                     //updateParticleImp(self, updateParticleSel, p, newPos);
1722 
1723                     // update particle counter
1724                     ++this._particleIdx;
1725                 } else {
1726                     // life < 0
1727                     var currentIndex = selParticle.atlasIndex;
1728                     cc.ArrayRemoveObject(this._particles, selParticle);
1729 
1730                     //cache particle to pool
1731                     this._particlePool.push(selParticle);
1732 
1733                     if (this._batchNode) {
1734                         //disable the switched particle
1735                         this._batchNode.disableParticle(this._atlasIndex + currentIndex);
1736 
1737                         //switch indexes
1738                         this._particles[this._particleCount - 1].atlasIndex = currentIndex;
1739                     }
1740 
1741                     --this._particleCount;
1742 
1743                     if (this._particleCount == 0 && this._isAutoRemoveOnFinish) {
1744                         this.unscheduleUpdate();
1745                         this._parent.removeChild(this, true);
1746                         return;
1747                     }
1748                 }
1749             }
1750             this._transformSystemDirty = false;
1751         }
1752 
1753         if (!this._batchNode)
1754             this.postStep();
1755 
1756         //cc.PROFILER_STOP_CATEGORY(kCCProfilerCategoryParticles , "cc.ParticleSystem - update");
1757     },
1758 
1759     updateWithNoTime:function () {
1760         this.update(0);
1761     },
1762 
1763     /**
1764      * return the string found by key in dict.
1765      * @param {string} key
1766      * @param {object} dict
1767      * @return {String} "" if not found; return the string if found.
1768      * @private
1769      */
1770     _valueForKey:function (key, dict) {
1771         if (dict) {
1772             var pString = dict[key];
1773             return pString != null ? pString : "";
1774         }
1775         return "";
1776     },
1777 
1778     _updateBlendFunc:function () {
1779         cc.Assert(!this._batchNode, "Can't change blending functions when the particle is being batched");
1780 
1781         if (this._texture) {
1782             if ((this._texture instanceof HTMLImageElement) || (this._texture instanceof HTMLCanvasElement)) {
1783 
1784             } else {
1785                 var premultiplied = this._texture.hasPremultipliedAlpha();
1786                 this._opacityModifyRGB = false;
1787 
1788                 if (this._texture && ( this._blendFunc.src == cc.BLEND_SRC && this._blendFunc.dst == cc.BLEND_DST )) {
1789                     if (premultiplied) {
1790                         this._opacityModifyRGB = true;
1791                     } else {
1792                         this._blendFunc.src = gl.SRC_ALPHA;
1793                         this._blendFunc.dst = gl.ONE_MINUS_SRC_ALPHA;
1794                     }
1795                 }
1796             }
1797         }
1798     }
1799 });
1800 
1801 //Compatibility with IE9
1802 (function () {
1803     var
1804         object = typeof window != 'undefined' ? window : exports,
1805         chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
1806         INVALID_CHARACTER_ERR = (function () {
1807             try { document.createElement('$'); }
1808             catch (error) { return error; }}());
1809 
1810     object.btoa || (
1811         object.btoa = function (input) {
1812             for (
1813                 // initialize result and counter
1814                 var block, charCode, idx = 0, map = chars, output = '';
1815                 // if the next input index does not exist:
1816                 //   change the mapping table to "="
1817                 //   check if d has no fractional digits
1818                 input.charAt(idx | 0) || (map = '=', idx % 1);
1819                 // "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8
1820                 output += map.charAt(63 & block >> 8 - idx % 1 * 8)
1821                 ) {
1822                 charCode = input.charCodeAt(idx += 3/4);
1823                 if (charCode > 0xFF) throw INVALID_CHARACTER_ERR;
1824                 block = block << 8 | charCode;
1825             }
1826             return output;
1827         });
1828 
1829     object.atob || (
1830         object.atob = function (input) {
1831             input = input.replace(/=+$/, '')
1832             if (input.length % 4 == 1) throw INVALID_CHARACTER_ERR;
1833             for (
1834                 // initialize result and counters
1835                 var bc = 0, bs, buffer, idx = 0, output = '';
1836                 // get next character
1837                 buffer = input.charAt(idx++);
1838                 // character found in table? initialize bit storage and add its ascii value;
1839                 ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
1840                     // and if not first of each 4 characters,
1841                     // convert the first 8 bits to one ascii character
1842                     bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
1843                 ) {
1844                 // try to find character in table (0-63, not found => -1)
1845                 buffer = chars.indexOf(buffer);
1846             }
1847             return output;
1848         });
1849 }());
1850 
1851 cc.encodeToBase64 = function (data) {
1852     return btoa(String.fromCharCode.apply(data, data)).replace(/.{76}(?=.)/g, '$&\n');
1853 };
1854 
1855 /**
1856  * <p> return the string found by key in dict. <br/>
1857  *    This plist files can be creted manually or with Particle Designer:<br/>
1858  *    http://particledesigner.71squared.com/<br/>
1859  * </p>
1860  * @param {String} plistFile
1861  * @return {cc.ParticleSystem}
1862  */
1863 cc.ParticleSystem.create = function (plistFile) {
1864     return cc.ParticleSystemQuad.create(plistFile);
1865 };
1866 
1867 cc.ParticleSystem.createWithTotalParticles = function (number_of_particles) {
1868     var emitter = cc.ParticleSystemQuad.create(number_of_particles);
1869     //emitter.initWithTotalParticles(number_of_particles);
1870     return emitter;
1871 };
1872 
1873 // Different modes
1874 /**
1875  * Mode A:Gravity + Tangential Accel + Radial Accel
1876  * @Class
1877  * @Construct
1878  * @param {cc.Point} gravity Gravity value.
1879  * @param {Number} speed speed of each particle.
1880  * @param {Number} speedVar speed variance of each particle.
1881  * @param {Number} tangentialAccel tangential acceleration of each particle.
1882  * @param {Number} tangentialAccelVar tangential acceleration variance of each particle.
1883  * @param {Number} radialAccel radial acceleration of each particle.
1884  * @param {Number} radialAccelVar radial acceleration variance of each particle.
1885  */
1886 cc.ParticleSystem.ModeA = function (gravity, speed, speedVar, tangentialAccel, tangentialAccelVar, radialAccel, radialAccelVar) {
1887     /** Gravity value. Only available in 'Gravity' mode. */
1888     this.gravity = gravity ? gravity : cc.PointZero();
1889     /** speed of each particle. Only available in 'Gravity' mode.  */
1890     this.speed = speed || 0;
1891     /** speed variance of each particle. Only available in 'Gravity' mode. */
1892     this.speedVar = speedVar || 0;
1893     /** tangential acceleration of each particle. Only available in 'Gravity' mode. */
1894     this.tangentialAccel = tangentialAccel || 0;
1895     /** tangential acceleration variance of each particle. Only available in 'Gravity' mode. */
1896     this.tangentialAccelVar = tangentialAccelVar || 0;
1897     /** radial acceleration of each particle. Only available in 'Gravity' mode. */
1898     this.radialAccel = radialAccel || 0;
1899     /** radial acceleration variance of each particle. Only available in 'Gravity' mode. */
1900     this.radialAccelVar = radialAccelVar || 0;
1901 };
1902 
1903 /**
1904  * Mode B: circular movement (gravity, radial accel and tangential accel don't are not used in this mode)
1905  * @Class
1906  * @Construct
1907  * @param {Number} startRadius The starting radius of the particles.
1908  * @param {Number} startRadiusVar The starting radius variance of the particles.
1909  * @param {Number} endRadius The ending radius of the particles.
1910  * @param {Number} endRadiusVar The ending radius variance of the particles.
1911  * @param {Number} rotatePerSecond Number of degress to rotate a particle around the source pos per second.
1912  * @param {Number} rotatePerSecondVar Variance in degrees for rotatePerSecond.
1913  */
1914 cc.ParticleSystem.ModeB = function (startRadius, startRadiusVar, endRadius, endRadiusVar, rotatePerSecond, rotatePerSecondVar) {
1915     /** The starting radius of the particles. Only available in 'Radius' mode. */
1916     this.startRadius = startRadius || 0;
1917     /** The starting radius variance of the particles. Only available in 'Radius' mode. */
1918     this.startRadiusVar = startRadiusVar || 0;
1919     /** The ending radius of the particles. Only available in 'Radius' mode. */
1920     this.endRadius = endRadius || 0;
1921     /** The ending radius variance of the particles. Only available in 'Radius' mode. */
1922     this.endRadiusVar = endRadiusVar || 0;
1923     /** Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode. */
1924     this.rotatePerSecond = rotatePerSecond || 0;
1925     /** Variance in degrees for rotatePerSecond. Only available in 'Radius' mode. */
1926     this.rotatePerSecondVar = rotatePerSecondVar || 0;
1927 };
1928