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      * Constructor
159      */
160     ctor:function () {
161         this._frames = [];
162     },
163 
164     // attributes
165 
166     /**
167      * return array of CCAnimationFrames
168      * @return {Array}
169      */
170     getFrames:function () {
171         return this._frames;
172     },
173 
174     /**
175      * array of CCAnimationFrames setter
176      * @param {Array} frames
177      */
178     setFrames:function (frames) {
179         this._frames = frames;
180     },
181 
182     /**
183      * adds a frame to a cc.Animation  The frame will be added with one "delay unit".
184      * @param {cc.SpriteFrame} frame
185      */
186     addSpriteFrame:function (frame) {
187         var animFrame = new cc.AnimationFrame();
188 
189         animFrame.initWithSpriteFrame(frame, 1, null);
190         this._frames.push(animFrame);
191         // update duration
192         this._totalDelayUnits++;
193     },
194 
195     /**
196      * 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".
197      * @param {String} fileName
198      */
199     addSpriteFrameWithFile:function (fileName) {
200         var texture = cc.textureCache.addImage(fileName);
201         var rect = cc.rect(0, 0, 0, 0);
202         rect.width = texture.width;
203         rect.height = texture.height;
204         var frame = cc.SpriteFrame.create(texture, rect);
205         this.addSpriteFrame(frame);
206     },
207 
208     /**
209      * 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".
210      * @param {cc.Texture2D} texture
211      * @param {cc.Rect} rect
212      */
213     addSpriteFrameWithTexture:function (texture, rect) {
214         var pFrame = cc.SpriteFrame.create(texture, rect);
215         this.addSpriteFrame(pFrame);
216     },
217 
218     /**
219      * Initializes a cc.Animation with cc.AnimationFrame
220      * @param {Array} arrayOfAnimationFrames
221      * @param {Number} delayPerUnit
222      * @param {Number} loops
223      */
224     initWithAnimationFrames:function (arrayOfAnimationFrames, delayPerUnit, loops) {
225         cc.arrayVerifyType(arrayOfAnimationFrames, cc.AnimationFrame);
226 
227         this._delayPerUnit = delayPerUnit;
228         this._loops = loops;
229         this._totalDelayUnits = 0;
230 
231         var locFrames = this._frames;
232         locFrames.length = 0;
233         for (var i = 0; i < arrayOfAnimationFrames.length; i++) {
234             var animFrame = arrayOfAnimationFrames[i];
235             locFrames.push(animFrame);
236             this._totalDelayUnits += animFrame.getDelayUnits();
237         }
238 
239         return true;
240     },
241 
242     clone: function(){
243         var animation = new cc.Animation();
244         animation.initWithAnimationFrames(this._copyFrames(), this._delayPerUnit, this._loops);
245         animation.setRestoreOriginalFrame(this._restoreOriginalFrame);
246         return animation;
247     },
248 
249     /**
250      * @param {cc.Animation} pZone
251      */
252     copyWithZone:function (pZone) {
253         var pCopy = new cc.Animation();
254         pCopy.initWithAnimationFrames(this._copyFrames(), this._delayPerUnit, this._loops);
255         pCopy.setRestoreOriginalFrame(this._restoreOriginalFrame);
256         return pCopy;
257     },
258 
259     _copyFrames:function(){
260        var copyFrames = [];
261         for(var i = 0; i< this._frames.length;i++)
262             copyFrames.push(this._frames[i].clone());
263         return copyFrames;
264     },
265 
266     copy:function (pZone) {
267         return this.copyWithZone(null);
268     },
269 
270     /**
271      * return how many times the animation is going to loop. 0 means animation is not animated. 1, animation is executed one time, ...
272      * @return {Number}
273      */
274     getLoops:function () {
275         return this._loops;
276     },
277 
278     /**
279      * set how many times the animation is going to loop. 0 means animation is not animated. 1, animation is executed one time, ...
280      * @param {Number} value
281      */
282     setLoops:function (value) {
283         this._loops = value;
284     },
285 
286     /**
287      * whether or not it shall restore the original frame when the animation finishes
288      * @param {Boolean} restOrigFrame
289      */
290     setRestoreOriginalFrame:function (restOrigFrame) {
291         this._restoreOriginalFrame = restOrigFrame;
292     },
293 
294     /**
295      * return whether or not it shall restore the original frame when the animation finishes
296      * @return {Boolean}
297      */
298     getRestoreOriginalFrame:function () {
299         return this._restoreOriginalFrame;
300     },
301 
302     /**
303      * return duration in seconds of the whole animation. It is the result of totalDelayUnits * delayPerUnit
304      * @return {Number}
305      */
306     getDuration:function () {
307         return this._totalDelayUnits * this._delayPerUnit;
308     },
309 
310     /**
311      * return Delay in seconds of the "delay unit"
312      * @return {Number}
313      */
314     getDelayPerUnit:function () {
315         return this._delayPerUnit;
316     },
317 
318     /**
319      * set Delay in seconds of the "delay unit"
320      * @param {Number} delayPerUnit
321      */
322     setDelayPerUnit:function (delayPerUnit) {
323         this._delayPerUnit = delayPerUnit;
324     },
325 
326     /**
327      * return total Delay units of the cc.Animation.
328      * @return {Number}
329      */
330     getTotalDelayUnits:function () {
331         return this._totalDelayUnits;
332     },
333 
334     /**
335      * Initializes a cc.Animation with frames and a delay between frames
336      * @param {Array} frames
337      * @param {Number} delay
338      */
339     initWithSpriteFrames:function (frames, delay) {
340         cc.arrayVerifyType(frames, cc.SpriteFrame);
341         this._loops = 1;
342         delay = delay || 0;
343         this._delayPerUnit = delay;
344         this._totalDelayUnits = 0;
345 
346         var locFrames = this._frames;
347         locFrames.length = 0;
348         if (frames) {
349             for (var i = 0; i < frames.length; i++) {
350                 var frame = frames[i];
351                 var animFrame = new cc.AnimationFrame();
352                 animFrame.initWithSpriteFrame(frame, 1, null);
353                 locFrames.push(animFrame);
354             }
355             this._totalDelayUnits += frames.length;
356         }
357         return true;
358     },
359     /**
360      * Currently JavaScript Bindings (JSB), in some cases, needs to use retain and release. This is a bug in JSB,
361      * and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB.
362      * This is a hack, and should be removed once JSB fixes the retain/release bug
363      */
364     retain:function () {
365     },
366     release:function () {
367     }
368 });
369 
370 /**
371  * Creates an animation.
372  * @param {Array} frames
373  * @param {Number} delay
374  * @param {Number} loops
375  * @return {cc.Animation}
376  * @example
377  * 1.
378  * //Creates an empty animation
379  * var animation1 = cc.Animation.create();
380  *
381  * 2.
382  * //Create an animation with sprite frames , delay and loops.
383  * var spriteFrames = [];
384  * var frame = cache.getSpriteFrame("grossini_dance_01.png");
385  * spriteFrames.push(frame);
386  * var animation1 = cc.Animation.create(spriteFrames);
387  * var animation2 = cc.Animation.create(spriteFrames, 0.2);
388  *
389  * 3.
390  * //Create an animation with animation frames , delay and loops.
391  * var animationFrames = [];
392  * var frame =  new cc.AnimationFrame();
393  * animationFrames.push(frame);
394  * var animation1 = cc.Animation.create(animationFrames);
395  * var animation2 = cc.Animation.create(animationFrames, 0.2);
396  * var animation3 = cc.Animation.create(animationFrames, 0.2,2);
397  *
398  */
399 cc.Animation.create = function (frames, delay, loops) {
400     var len = arguments.length;
401     var animation = new cc.Animation();
402     if (len == 0) {
403         animation.initWithSpriteFrames(null, 0);
404     } else {
405         var frame0 = frames[0];
406         if(frame0){
407             if (frame0 instanceof cc.SpriteFrame) {
408                 //init with sprite frames , delay and loops.
409                 animation.initWithSpriteFrames(frames, delay);
410             }else if(frame0 instanceof cc.AnimationFrame) {
411                 //init with sprite frames , delay and loops.
412                 animation.initWithAnimationFrames(frames, delay, loops);
413             }
414         }
415     }
416     return animation;
417 };
418