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