1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2008-2010 Ricardo Quesada
  4  Copyright (c) 2011      Zynga Inc.
  5 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25  ****************************************************************************/
 26 
 27 /**
 28  * <p>
 29  *    cc.AnimationFrame
 30  *    A frame of the animation. It contains information like:
 31  *       - sprite frame name
 32  *       - # of delay units.
 33  *       - offset
 34  * </p>
 35  * @class
 36  * @extends cc.Class
 37  */
 38 cc.AnimationFrame = cc.Class.extend(/** @lends cc.AnimationFrame# */{
 39     _spriteFrame:null,
 40     _delayPerUnit:0,
 41     _userInfo:null,
 42 
 43     ctor:function () {
 44         this._delayPerUnit = 0;
 45     },
 46 
 47     clone: function(){
 48         var frame = new cc.AnimationFrame();
 49         frame.initWithSpriteFrame(this._spriteFrame.clone(), this._delayPerUnit, this._userInfo);
 50         return frame;
 51     },
 52 
 53     copyWithZone:function (pZone) {
 54         return cc.clone(this);
 55     },
 56 
 57     copy:function (pZone) {
 58         var newFrame = new cc.AnimationFrame();
 59         newFrame.initWithSpriteFrame(this._spriteFrame.clone(), this._delayPerUnit, this._userInfo);
 60         return newFrame;
 61     },
 62 
 63     /**
 64      * initializes the animation frame with a spriteframe, number of delay units and a notification user info
 65      * @param {cc.SpriteFrame} spriteFrame
 66      * @param {Number} delayUnits
 67      * @param {object} userInfo
 68      */
 69     initWithSpriteFrame:function (spriteFrame, delayUnits, userInfo) {
 70         this._spriteFrame = spriteFrame;
 71         this._delayPerUnit = delayUnits;
 72         this._userInfo = userInfo;
 73 
 74         return true;
 75     },
 76 
 77     /**
 78      * cc.SpriteFrameName to be used
 79      * @return {cc.SpriteFrame}
 80      */
 81     getSpriteFrame:function () {
 82         return this._spriteFrame;
 83     },
 84 
 85     /**
 86      * cc.SpriteFrameName to be used
 87      * @param {cc.SpriteFrame} spriteFrame
 88      */
 89     setSpriteFrame:function (spriteFrame) {
 90         this._spriteFrame = spriteFrame;
 91     },
 92 
 93     /**
 94      * how many units of time the frame takes getter
 95      * @return {Number}
 96      */
 97     getDelayUnits:function () {
 98         return this._delayPerUnit;
 99     },
100 
101     /**
102      *  how many units of time the frame takes setter
103      * @param delayUnits
104      */
105     setDelayUnits:function (delayUnits) {
106         this._delayPerUnit = delayUnits;
107     },
108 
109     /**
110      *  <p>A cc.AnimationFrameDisplayedNotification notification will be broadcasted when the frame is displayed with this dictionary as UserInfo.<br/>
111      *  If UserInfo is nil, then no notification will be broadcasted. </p>
112      * @return {object}
113      */
114     getUserInfo:function () {
115         return this._userInfo;
116     },
117 
118     /**
119      * @param {object} userInfo
120      */
121     setUserInfo:function (userInfo) {
122         this._userInfo = userInfo;
123     }
124 });
125 
126 /**
127  * <p>
128  *     A cc.Animation object is used to perform animations on the cc.Sprite objects.<br/>
129  *     <br/>
130  *      The cc.Animation object contains cc.SpriteFrame objects, and a possible delay between the frames. <br/>
131  *      You can animate a cc.Animation object by using the cc.Animate action. Example:  <br/>
132  * </p>
133  * @class
134  * @extends cc.Class
135  *
136  * @example
137  * //create an animation object
138  * var animation = cc.Animation.create();
139  *
140  * //add a sprite frame to this animation
141  * animation.addFrameWithFile("grossini_dance_01.png");
142  *
143  * //create an animate with this animation
144  * var action = cc.Animate.create(animation);
145  *
146  * //run animate
147  * this._grossini.runAction(action);
148  */
149 cc.Animation = cc.Class.extend(/** @lends cc.Animation# */{
150     _frames:null,
151     _loops:0,
152     _restoreOriginalFrame:false,
153     _duration:0,
154     _delayPerUnit:0,
155     _totalDelayUnits:0,
156 
157 	/**
158 	 * Creates an animation.
159 	 * @constructor
160 	 * @param {Array} frames
161 	 * @param {Number} delay
162 	 * @param {Number} [loops=1]
163 	 * @example
164 	 * // 1. Creates an empty animation
165 	 * var animation1 = new cc.Animation();
166 	 *
167 	 * // 2. Create an animation with sprite frames, delay and loops.
168 	 * var spriteFrames = [];
169 	 * var frame = cache.getSpriteFrame("grossini_dance_01.png");
170 	 * spriteFrames.push(frame);
171 	 * var animation1 = new cc.Animation(spriteFrames);
172 	 * var animation2 = new cc.Animation(spriteFrames, 0.2);
173 	 * var animation2 = new cc.Animation(spriteFrames, 0.2, 2);
174 	 *
175 	 * // 3. Create an animation with animation frames, delay and loops.
176 	 * var animationFrames = [];
177 	 * var frame =  new cc.AnimationFrame();
178 	 * animationFrames.push(frame);
179 	 * var animation1 = new cc.Animation(animationFrames);
180 	 * var animation2 = new cc.Animation(animationFrames, 0.2);
181 	 * var animation3 = new cc.Animation(animationFrames, 0.2, 2);
182 	 */
183     ctor:function (frames, delay, loops) {
184         this._frames = [];
185 
186 		if (frames === undefined) {
187 			this.initWithSpriteFrames(null, 0);
188 		} else {
189 			var frame0 = frames[0];
190 			if(frame0){
191 				if (frame0 instanceof cc.SpriteFrame) {
192 					//init with sprite frames , delay and loops.
193 					this.initWithSpriteFrames(frames, delay, loops);
194 				}else if(frame0 instanceof cc.AnimationFrame) {
195 					//init with sprite frames , delay and loops.
196 					this.initWithAnimationFrames(frames, delay, loops);
197 				}
198 			}
199 		}
200     },
201 
202     // attributes
203 
204     /**
205      * return array of CCAnimationFrames
206      * @return {Array}
207      */
208     getFrames:function () {
209         return this._frames;
210     },
211 
212     /**
213      * array of CCAnimationFrames setter
214      * @param {Array} frames
215      */
216     setFrames:function (frames) {
217         this._frames = frames;
218     },
219 
220     /**
221      * adds a frame to a cc.Animation  The frame will be added with one "delay unit".
222      * @param {cc.SpriteFrame} frame
223      */
224     addSpriteFrame:function (frame) {
225         var animFrame = new cc.AnimationFrame();
226 
227         animFrame.initWithSpriteFrame(frame, 1, null);
228         this._frames.push(animFrame);
229         // update duration
230         this._totalDelayUnits++;
231     },
232 
233     /**
234      * Adds a frame with an image filename. Internally it will create a cc.SpriteFrame and it will add it. The frame will be added with one "delay unit".
235      * @param {String} fileName
236      */
237     addSpriteFrameWithFile:function (fileName) {
238         var texture = cc.textureCache.addImage(fileName);
239         var rect = cc.rect(0, 0, 0, 0);
240         rect.width = texture.width;
241         rect.height = texture.height;
242         var frame = cc.SpriteFrame.create(texture, rect);
243         this.addSpriteFrame(frame);
244     },
245 
246     /**
247      * Adds a frame with a texture and a rect. Internally it will create a cc.SpriteFrame and it will add it. The frame will be added with one "delay unit".
248      * @param {cc.Texture2D} texture
249      * @param {cc.Rect} rect
250      */
251     addSpriteFrameWithTexture:function (texture, rect) {
252         var pFrame = cc.SpriteFrame.create(texture, rect);
253         this.addSpriteFrame(pFrame);
254     },
255 
256     /**
257      * Initializes a cc.Animation with cc.AnimationFrame
258      * @param {Array} arrayOfAnimationFrames
259      * @param {Number} delayPerUnit
260      * @param {Number} [loops=1]
261      */
262     initWithAnimationFrames:function (arrayOfAnimationFrames, delayPerUnit, loops) {
263         cc.arrayVerifyType(arrayOfAnimationFrames, cc.AnimationFrame);
264 
265         this._delayPerUnit = delayPerUnit;
266         this._loops = loops === undefined ? 1 : loops;
267         this._totalDelayUnits = 0;
268 
269         var locFrames = this._frames;
270         locFrames.length = 0;
271         for (var i = 0; i < arrayOfAnimationFrames.length; i++) {
272             var animFrame = arrayOfAnimationFrames[i];
273             locFrames.push(animFrame);
274             this._totalDelayUnits += animFrame.getDelayUnits();
275         }
276 
277         return true;
278     },
279 
280     clone: function(){
281         var animation = new cc.Animation();
282         animation.initWithAnimationFrames(this._copyFrames(), this._delayPerUnit, this._loops);
283         animation.setRestoreOriginalFrame(this._restoreOriginalFrame);
284         return animation;
285     },
286 
287     /**
288      * @param {cc.Animation} pZone
289      */
290     copyWithZone:function (pZone) {
291         var pCopy = new cc.Animation();
292         pCopy.initWithAnimationFrames(this._copyFrames(), this._delayPerUnit, this._loops);
293         pCopy.setRestoreOriginalFrame(this._restoreOriginalFrame);
294         return pCopy;
295     },
296 
297     _copyFrames:function(){
298        var copyFrames = [];
299         for(var i = 0; i< this._frames.length;i++)
300             copyFrames.push(this._frames[i].clone());
301         return copyFrames;
302     },
303 
304     copy:function (pZone) {
305         return this.copyWithZone(null);
306     },
307 
308     /**
309      * return how many times the animation is going to loop. 0 means animation is not animated. 1, animation is executed one time, ...
310      * @return {Number}
311      */
312     getLoops:function () {
313         return this._loops;
314     },
315 
316     /**
317      * set how many times the animation is going to loop. 0 means animation is not animated. 1, animation is executed one time, ...
318      * @param {Number} value
319      */
320     setLoops:function (value) {
321         this._loops = value;
322     },
323 
324     /**
325      * whether or not it shall restore the original frame when the animation finishes
326      * @param {Boolean} restOrigFrame
327      */
328     setRestoreOriginalFrame:function (restOrigFrame) {
329         this._restoreOriginalFrame = restOrigFrame;
330     },
331 
332     /**
333      * return whether or not it shall restore the original frame when the animation finishes
334      * @return {Boolean}
335      */
336     getRestoreOriginalFrame:function () {
337         return this._restoreOriginalFrame;
338     },
339 
340     /**
341      * return duration in seconds of the whole animation. It is the result of totalDelayUnits * delayPerUnit
342      * @return {Number}
343      */
344     getDuration:function () {
345         return this._totalDelayUnits * this._delayPerUnit;
346     },
347 
348     /**
349      * return Delay in seconds of the "delay unit"
350      * @return {Number}
351      */
352     getDelayPerUnit:function () {
353         return this._delayPerUnit;
354     },
355 
356     /**
357      * set Delay in seconds of the "delay unit"
358      * @param {Number} delayPerUnit
359      */
360     setDelayPerUnit:function (delayPerUnit) {
361         this._delayPerUnit = delayPerUnit;
362     },
363 
364     /**
365      * return total Delay units of the cc.Animation.
366      * @return {Number}
367      */
368     getTotalDelayUnits:function () {
369         return this._totalDelayUnits;
370     },
371 
372     /**
373      * Initializes a cc.Animation with frames and a delay between frames
374      * @param {Array} frames
375      * @param {Number} delay
376      * @param {Number} [loops=1]
377      */
378     initWithSpriteFrames:function (frames, delay, loops) {
379         cc.arrayVerifyType(frames, cc.SpriteFrame);
380         this._loops = loops === undefined ? 1 : loops;
381         this._delayPerUnit = delay || 0;
382         this._totalDelayUnits = 0;
383 
384         var locFrames = this._frames;
385         locFrames.length = 0;
386         if (frames) {
387             for (var i = 0; i < frames.length; i++) {
388                 var frame = frames[i];
389                 var animFrame = new cc.AnimationFrame();
390                 animFrame.initWithSpriteFrame(frame, 1, null);
391                 locFrames.push(animFrame);
392             }
393             this._totalDelayUnits += frames.length;
394         }
395         return true;
396     },
397     /**
398      * Currently JavaScript Bindings (JSB), in some cases, needs to use retain and release. This is a bug in JSB,
399      * and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB.
400      * This is a hack, and should be removed once JSB fixes the retain/release bug
401      */
402     retain:function () {
403     },
404     release:function () {
405     }
406 });
407 
408 /**
409  * Creates an animation.
410  * @param {Array} frames
411  * @param {Number} delay
412  * @param {Number} [loops=1]
413  * @return {cc.Animation}
414  * @example
415  * 1.
416  * //Creates an empty animation
417  * var animation1 = cc.Animation.create();
418  *
419  * 2.
420  * //Create an animation with sprite frames , delay and loops.
421  * var spriteFrames = [];
422  * var frame = cache.getSpriteFrame("grossini_dance_01.png");
423  * spriteFrames.push(frame);
424  * var animation1 = cc.Animation.create(spriteFrames);
425  * var animation2 = cc.Animation.create(spriteFrames, 0.2);
426  * var animation2 = cc.Animation.create(spriteFrames, 0.2, 2);
427  *
428  * 3.
429  * //Create an animation with animation frames , delay and loops.
430  * var animationFrames = [];
431  * var frame =  new cc.AnimationFrame();
432  * animationFrames.push(frame);
433  * var animation1 = cc.Animation.create(animationFrames);
434  * var animation2 = cc.Animation.create(animationFrames, 0.2);
435  * var animation3 = cc.Animation.create(animationFrames, 0.2, 2);
436  */
437 cc.Animation.create = function (frames, delay, loops) {
438     return new cc.Animation(frames, delay, loops);
439 };
440