1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3 
  4  http://www.cocos2d-x.org
  5 
  6  Permission is hereby granted, free of charge, to any person obtaining a copy
  7  of this software and associated documentation files (the "Software"), to deal
  8  in the Software without restriction, including without limitation the rights
  9  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10  copies of the Software, and to permit persons to whom the Software is
 11  furnished to do so, subject to the following conditions:
 12 
 13  The above copyright notice and this permission notice shall be included in
 14  all copies or substantial portions of the Software.
 15 
 16  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 22  THE SOFTWARE.
 23  ****************************************************************************/
 24 
 25 /**
 26  * movement event type
 27  * @type {Object}
 28  */
 29 ccs.MovementEventType = {
 30     start: 0,
 31     complete: 1,
 32     loopComplete: 2
 33 };
 34 /**
 35  * Base class for cc.MovementEvent objects.
 36  * @class
 37  * @extends ccs.Class
 38  */
 39 ccs.AnimationEvent = ccs.Class.extend(/** @lends ccs.AnimationEvent# */{
 40     _arguments:null,
 41     _callFunc:null,
 42     _selectorTarget:null,
 43     ctor:function (target, callFunc, data) {
 44         this._data = data;
 45         this._callFunc = callFunc;
 46         this._selectorTarget = target;
 47     },
 48     call:function () {
 49         if (this._callFunc) {
 50             this._callFunc.apply(this._selectorTarget, this._arguments);
 51         }
 52     },
 53     setArguments:function (args) {
 54         this._arguments = args;
 55     }
 56 });
 57 /**
 58  * movement event
 59  * @constructor
 60  */
 61 ccs.MovementEvent = function () {
 62     this.armature = null;
 63     this.movementType = "";
 64     this.movementID = "";
 65 };
 66 /**
 67  * frame event
 68  * @constructor
 69  */
 70 ccs.FrameEvent = function () {
 71     this.bone = null;
 72     this.frameEventName = "";
 73     this.originFrameIndex = 0;
 74     this.currentFrameIndex = 0;
 75 };
 76 /**
 77  * Base class for ccs.ArmatureAnimation objects.
 78  * @class
 79  * @extends ccs.ProcessBase
 80  * 
 81  * @property {ccs.AnimationData}    animationData       - Animation data
 82  * @property {Object}               userObject          - User custom object
 83  * @property {Boolean}              ignoreFrameEvent    - Indicate whether the frame event is ignored
 84  * @property {Number}               speedScale          - Animation play speed scale
 85  * @property {Number}               animationScale      - Animation play speed scale
 86  * 
 87  */
 88 ccs.ArmatureAnimation = ccs.ProcessBase.extend(/** @lends ccs.ArmatureAnimation# */{
 89     animationData:null,
 90     _movementData:null,
 91     _armature:null,
 92     _movementID:"",
 93     _prevFrameIndex:0,
 94     _toIndex:0,
 95     _tweenList:null,
 96     _frameEvent:null,
 97     _movementEvent:null,
 98     _speedScale:1,
 99     ignoreFrameEvent:false,
100     _frameEventQueue:null,
101     _movementEventQueue:null,
102     userObject:null,
103     _movementList: null,
104     _onMovementList: false,
105     _movementListLoop: false,
106     _movementIndex: 0,
107     ctor:function () {
108         ccs.ProcessBase.prototype.ctor.call(this);
109         this.animationData = null;
110         this._movementData = null;
111         this._movementID = "";
112         this._armature = null;
113         this._prevFrameIndex = 0;
114         this._toIndex = 0;
115         this._tweenList = [];
116         this._frameEvent = null;
117         this._movementEvent = null;
118         this._speedScale = 1;
119         this.ignoreFrameEvent = false;
120         this._frameEventQueue = [];
121         this._movementEventQueue = [];
122         this.userObject = null;
123         this._movementList = [];
124         this._onMovementList = false;
125         this._movementListLoop = false;
126         this._movementIndex = 0;
127     },
128 
129     /**
130      * init with a CCArmature
131      * @param {ccs.Armature} armature
132      * @return {Boolean}
133      */
134     init:function (armature) {
135         this._armature = armature;
136         this._tweenList = [];
137         return true;
138     },
139     pause:function () {
140         for (var i = 0; i < this._tweenList.length; i++) {
141             this._tweenList[i].pause();
142         }
143         ccs.ProcessBase.prototype.pause.call(this);
144     },
145     resume:function () {
146         for (var i = 0; i < this._tweenList.length; i++) {
147             this._tweenList[i].resume();
148         }
149         ccs.ProcessBase.prototype.resume.call(this);
150     },
151 
152     stop:function () {
153         for (var i = 0; i < this._tweenList.length; i++) {
154             this._tweenList[i].stop();
155         }
156         this._tweenList = [];
157         ccs.ProcessBase.prototype.stop.call(this);
158     },
159 
160     /**
161      * scale animation play speed
162      * @param {Number} speedScale
163      */
164     setSpeedScale:function (speedScale) {
165         if (speedScale == this._speedScale) {
166             return;
167         }
168         this._speedScale = speedScale;
169         this._processScale = !this._movementData ? this._speedScale : this._speedScale * this._movementData.scale;
170         var dict = this._armature.getBoneDic();
171         for (var key in dict) {
172             var bone = dict[key];
173             bone.getTween().setProcessScale(this._processScale);
174             if (bone.getChildArmature()) {
175                 bone.getChildArmature().getAnimation().setProcessScale(this._processScale);
176             }
177         }
178     },
179 
180     getSpeedScale:function(){
181         return this._speedScale;
182     },
183 
184     getAnimationScale:function(){
185         return this.getSpeedScale();
186     },
187     setAnimationScale:function(animationScale){
188         return this.setSpeedScale(animationScale);
189     },
190 
191     /**
192      * play animation by animation name.
193      * @param {String} animationName The animation name you want to play
194      * @param {Number} durationTo
195      *         he frames between two animation changing-over.It's meaning is changing to this animation need how many frames
196      *         -1 : use the value from CCMovementData get from flash design panel
197      * @param {Number} durationTween he
198      *         frame count you want to play in the game.if  _durationTween is 80, then the animation will played 80 frames in a loop
199      *         -1 : use the value from CCMovementData get from flash design panel
200      * @param {Number} loop
201      *          Whether the animation is loop.
202      *         loop < 0 : use the value from CCMovementData get from flash design panel
203      *         loop = 0 : this animation is not loop
204      *         loop > 0 : this animation is loop
205      * @param {Number} tweenEasing
206      *          CCTween easing is used for calculate easing effect
207      *         TWEEN_EASING_MAX : use the value from CCMovementData get from flash design panel
208      *         -1 : fade out
209      *         0  : line
210      *         1  : fade in
211      *         2  : fade in and out
212      * @example
213      * // example
214      * armature.getAnimation().play("run",-1,-1,1,-1);//loop play
215      * armature.getAnimation().play("run",-1,-1,0,-1);//not loop play
216      */
217     play:function (animationName, durationTo, durationTween, loop, tweenEasing) {
218         if (this.animationData == null) {
219             cc.log("this.animationData can not be null");
220             return;
221         }
222         this._movementData = this.animationData.getMovement(animationName);
223         if (this._movementData == null) {
224             cc.log("this._movementData can not be null");
225             return;
226         }
227         if (typeof durationTo == "undefined") {
228             durationTo = -1;
229         }
230 
231         if (typeof loop == "undefined") {
232             loop = -1;
233         }
234 
235         var locMovementData = this._movementData;
236         //Get key frame count
237         this._rawDuration = locMovementData.duration;
238         this._movementID = animationName;
239         this._processScale = this._speedScale * locMovementData.scale;
240         //Further processing parameters
241         durationTo = (durationTo == -1) ? locMovementData.durationTo : durationTo;
242         var durationTween = locMovementData.durationTween;
243         durationTween = (durationTween == 0) ? locMovementData.duration : durationTween;//todo
244         var tweenEasing = locMovementData.tweenEasing;
245 
246         if (loop < 0) {
247             loop = locMovementData.loop;
248         } else {
249             loop = Boolean(loop);
250         }
251 
252         this._onMovementList = false;
253         ccs.ProcessBase.prototype.play.call(this, durationTo, tweenEasing);
254 
255         if (this._rawDuration == 0) {
256             this._loopType = ccs.ANIMATION_TYPE_SINGLE_FRAME;
257         }
258         else {
259             if (loop) {
260                 this._loopType = ccs.ANIMATION_TYPE_TO_LOOP_FRONT;
261             }
262             else {
263                 this._loopType = ccs.ANIMATION_TYPE_NO_LOOP;
264             }
265             this._durationTween = durationTween;
266         }
267 
268         this._tweenList = [];
269 
270         var movementBoneData;
271         var dict = this._armature.getBoneDic();
272         for (var key in dict) {
273             var bone = dict[key];
274             movementBoneData = locMovementData.getMovementBoneData(bone.getName());
275             var tween = bone.getTween();
276             if (movementBoneData && movementBoneData.frameList.length > 0) {
277                 this._tweenList.push(tween);
278                 movementBoneData.duration = locMovementData.duration;
279                 tween.play(movementBoneData, durationTo, durationTween, loop, tweenEasing);
280 
281                 tween.setProcessScale(this._processScale);
282                 if (bone.getChildArmature()) {
283                     bone.getChildArmature().getAnimation().setProcessScale(this._processScale);
284                 }
285             } else {
286                 if (!bone.getIgnoreMovementBoneData()) {
287                     bone.getDisplayManager().changeDisplayWithIndex(-1, false);
288                     tween.stop();
289                 }
290             }
291         }
292         this._armature.update(0);
293     },
294 
295     /**
296      * play with names
297      * @param {Array} movementNames
298      * @param {Number} durationTo
299      * @param {Boolean} loop
300      */
301     playWithNames: function (movementNames, durationTo, loop) {
302         this._movementList = [];
303         this._movementListLoop = loop;
304         this._onMovementList = true;
305         this._movementIndex = 0;
306 
307         for (var i = 0; i < movementNames.length; i++) {
308             this._movementList.push({name: movementNames[i], durationTo: durationTo});
309         }
310 
311         this.updateMovementList();
312     },
313 
314     updateMovementList: function () {
315         if (this._onMovementList) {
316             if (this._movementListLoop) {
317                 var movementObj = this._movementList[this._movementIndex];
318                 this.play(movementObj.name, movementObj.durationTo,-1,0);
319                 this._movementIndex++;
320                 if (this._movementIndex >= this._movementList.length) {
321                     this._movementIndex = 0;
322                 }
323             }
324             else {
325                 if (this._movementIndex < this._movementList.length) {
326                     var movementObj = this._movementList[this._movementIndex];
327                     this.play(movementObj.name, movementObj.durationTo,-1,0);
328                     this._movementIndex++;
329                 }
330                 else {
331                     this._onMovementList = false;
332                 }
333             }
334             this._onMovementList = true;
335         }
336     },
337     
338     /**
339      * Go to specified frame and play current movement.
340      * You need first switch to the movement you want to play, then call this function.
341      *
342      * example : playByIndex(0);
343      *           gotoAndPlay(0);
344      *           playByIndex(1);
345      *           gotoAndPlay(0);
346      *           gotoAndPlay(15);
347      * @param {Number} frameIndex
348      */
349     gotoAndPlay: function (frameIndex) {
350         if (!this._movementData || frameIndex < 0 || frameIndex >= this._movementData.duration) {
351             cc.log("Please ensure you have played a movement, and the frameIndex is in the range.");
352             return;
353         }
354 
355         var ignoreFrameEvent = this.ignoreFrameEvent;
356         this.ignoreFrameEvent = true;
357         this._isPlaying = true;
358         this._isComplete = this._isPause = false;
359 
360         ccs.ProcessBase.prototype.gotoFrame.call(this, frameIndex);
361         this._currentPercent = this._curFrameIndex / (this._movementData.duration - 1);
362         this._currentFrame = this._nextFrameIndex * this._currentPercent;
363 
364         for (var i = 0; i < this._tweenList.length; i++) {
365             var tween = this._tweenList[i];
366             tween.gotoAndPlay(frameIndex);
367         }
368         this._armature.update(0);
369         this.ignoreFrameEvent = ignoreFrameEvent;
370     },
371 
372     /**
373      * Go to specified frame and pause current movement.
374      * @param {Number} frameIndex
375      */
376     gotoAndPause: function (frameIndex) {
377         this.gotoAndPlay(frameIndex);
378         this.pause();
379     },
380 
381     /**
382      * Play animation with index, the other param is the same to play.
383      * @param {Number||Array} animationIndex
384      * @param {Number} durationTo
385      * @param {Number} durationTween
386      * @param {Number} loop
387      * @param {Number} tweenEasing
388      */
389     playWithIndex:function (animationIndex, durationTo, durationTween, loop, tweenEasing) {
390         if (typeof durationTo == "undefined") {
391             durationTo = -1;
392         }
393         if (typeof loop == "undefined") {
394             loop = -1;
395         }
396         var moveNames = this.animationData.movementNames;
397         if (animationIndex < -1 || animationIndex >= moveNames.length) {
398             return;
399         }
400         var animationName = moveNames[animationIndex];
401         this.play(animationName, durationTo,-1, loop, 0);
402     },
403 
404     /**
405      * Play animation with index, the o ther param is the same to play.
406      * @param {Number} animationIndex
407      * @param {Number} durationTo
408      * @param {Number} durationTween
409      * @param {Number} loop
410      * @param {Number} tweenEasing
411      */
412     playByIndex:function(animationIndex, durationTo, durationTween, loop, tweenEasing){
413         cc.log("playByIndex is deprecated. Use playWithIndex instead.");
414         this.playWithIndex(animationIndex, durationTo, durationTween, loop, tweenEasing);
415     },
416 
417     /**
418      *  play by indexes
419      * @param movementIndexes
420      * @param {Number} durationTo
421      * @param {Boolean} loop
422      */
423     playWithIndexes: function (movementIndexes, durationTo, loop) {
424         this._movementList = [];
425         this._movementListLoop = loop;
426         this._onMovementList = true;
427         this._movementIndex = 0;
428 
429         var movName = this.animationData.movementNames;
430 
431         for (var i = 0; i < movementIndexes.length; i++) {
432             var name = movName[movementIndexes[i]];
433             this._movementList.push({name: name, durationTo: durationTo});
434         }
435 
436         this.updateMovementList();
437     },
438 
439     /**
440      * get movement count
441      * @return {Number}
442      */
443     getMovementCount:function () {
444         return this.animationData.getMovementCount();
445     },
446 
447     update:function (dt) {
448         if(ccs.ProcessBase.prototype.update.call(this, dt)){
449             for (var i = 0; i < this._tweenList.length; i++) {
450                 this._tweenList[i].update(dt);
451             }
452         }
453 
454         var frameEvents = this._frameEventQueue;
455         while (frameEvents.length > 0) {
456             var frameEvent = frameEvents.shift();
457             this.ignoreFrameEvent = true;
458             this.callFrameEvent([frameEvent.bone, frameEvent.frameEventName, frameEvent.originFrameIndex, frameEvent.currentFrameIndex]);
459             this.ignoreFrameEvent = false;
460         }
461 
462         var movementEvents = this._movementEventQueue;
463         while (movementEvents.length > 0) {
464             var movEvent = movementEvents.shift();
465             this.callMovementEvent([movEvent.armature, movEvent.movementType, movEvent.movementID]);
466         }
467     },
468 
469     /**
470      * update will call this handler, you can handle your logic here
471      */
472     updateHandler:function () {
473         var locCurrentPercent = this._currentPercent;
474         if (locCurrentPercent >= 1) {
475             switch (this._loopType) {
476                 case ccs.ANIMATION_TYPE_NO_LOOP:
477                     this._loopType = ccs.ANIMATION_TYPE_MAX;
478                     this._currentFrame = (locCurrentPercent - 1) * this._nextFrameIndex;
479                     locCurrentPercent = this._currentFrame / this._durationTween;
480                     if (locCurrentPercent < 1.0) {
481                         this._nextFrameIndex = this._durationTween;
482                         this.movementEvent(this._armature, ccs.MovementEventType.start, this._movementID);
483                         break;
484                     }
485                 case ccs.ANIMATION_TYPE_MAX:
486                 case ccs.ANIMATION_TYPE_SINGLE_FRAME:
487                     locCurrentPercent = 1;
488                     this._isComplete = true;
489                     this._isPlaying = false;
490                     this.movementEvent(this._armature, ccs.MovementEventType.complete, this._movementID);
491                     this.updateMovementList();
492                     break;
493                 case ccs.ANIMATION_TYPE_TO_LOOP_FRONT:
494                     this._loopType = ccs.ANIMATION_TYPE_LOOP_FRONT;
495                     locCurrentPercent = ccs.fmodf(locCurrentPercent, 1);
496                     this._currentFrame = this._nextFrameIndex == 0 ? 0 : ccs.fmodf(this._currentFrame, this._nextFrameIndex);
497                     this._nextFrameIndex = this._durationTween > 0 ? this._durationTween : 1;
498                     this.movementEvent(this, ccs.MovementEventType.start, this._movementID);
499                     break;
500                 default:
501                     //locCurrentPercent = ccs.fmodf(locCurrentPercent, 1);
502                     this._currentFrame = ccs.fmodf(this._currentFrame, this._nextFrameIndex);
503                     this._toIndex = 0;
504                     this.movementEvent(this._armature, ccs.MovementEventType.loopComplete, this._movementID);
505                     break;
506             }
507             this._currentPercent = locCurrentPercent;
508         }
509     },
510 
511     /**
512      * Get current movementID
513      * @returns {String}
514      */
515     getCurrentMovementID: function () {
516         if (this._isComplete)
517             return "";
518         return this._movementID;
519     },
520 
521     /**
522      * connect a event
523      * @param {Object} target
524      * @param {function} callFunc
525      */
526     setMovementEventCallFunc:function (callFunc, target) {
527         this._movementEvent = new ccs.AnimationEvent(target, callFunc);
528     },
529 
530     /**
531      * call event
532      * @param {Array} args
533      */
534     callMovementEvent:function (args) {
535         if (this._movementEvent) {
536             this._movementEvent.setArguments(args);
537             this._movementEvent.call();
538         }
539     },
540 
541     /**
542      * connect a event
543      * @param {Object} target
544      * @param {function} callFunc
545      */
546     setFrameEventCallFunc:function (callFunc, target) {
547         this._frameEvent = new ccs.AnimationEvent(target, callFunc);
548     },
549 
550     /**
551      * call event
552      * @param {Array} args
553      */
554     callFrameEvent:function (args) {
555         if (this._frameEvent) {
556             this._frameEvent.setArguments(args);
557             this._frameEvent.call();
558         }
559     },
560 
561     movementEvent:function(armature, movementType,  movementID){
562         if (this._movementEvent)    {
563             var event = new ccs.MovementEvent();
564             event.armature = armature;
565             event.movementType = movementType;
566             event.movementID = movementID;
567             this._movementEventQueue.push(event);
568         }
569     },
570 
571     /**
572      * @param {ccs.Bone} bone
573      * @param {String} frameEventName
574      * @param {Number} originFrameIndex
575      * @param {Number} currentFrameIndex
576      */
577     frameEvent:function(bone, frameEventName,  originFrameIndex,  currentFrameIndex){
578         if (this._frameEvent)    {
579             var frameEvent = new ccs.FrameEvent();
580             frameEvent.bone = bone;
581             frameEvent.frameEventName = frameEventName;
582             frameEvent.originFrameIndex = originFrameIndex;
583             frameEvent.currentFrameIndex = currentFrameIndex;
584             this._frameEventQueue.push(frameEvent);
585         }
586     },
587     
588     /**
589      * animationData setter
590      * @param {ccs.AnimationData} aniData
591      */
592     setAnimationData:function (aniData) {
593         this.animationData = aniData;
594     },
595 
596     /**
597      * animationData getter
598      * @return {ccs.AnimationData}
599      */
600     getAnimationData:function () {
601         return this.animationData;
602     },
603     /**
604      * userObject setter
605      * @param {Object} userObject
606      */
607     setUserObject:function (userObject) {
608         this.userObject = userObject;
609     },
610 
611     /**
612      * userObject getter
613      * @return {Object}
614      */
615     getUserObject:function () {
616         return this.userObject;
617     },
618 
619     /**
620      * Determines if the frame event is ignored
621      * @returns {boolean}
622      */
623     isIgnoreFrameEvent:function(){
624         return this.ignoreFrameEvent;
625     },
626 
627     /**
628      * Sets whether the frame event is ignored
629      * @param {Boolean} bool
630      */
631     setIgnoreFrameEvent:function(bool){
632         this.ignoreFrameEvent = bool;
633     }
634 });
635 
636 window._p = ccs.ArmatureAnimation.prototype;
637 
638 // Extended properties
639 /** @expose */
640 _p.speedScale;
641 cc.defineGetterSetter(_p, "speedScale", _p.getSpeedScale, _p.setSpeedScale);
642 /** @expose */
643 _p.animationScale;
644 cc.defineGetterSetter(_p, "animationScale", _p.getAnimationScale, _p.setAnimationScale);
645 
646 delete window._p;
647 
648 /**
649  * allocates and initializes a ArmatureAnimation.
650  * @constructs
651  * @return {ccs.ArmatureAnimation}
652  * @example
653  * // example
654  * var animation = ccs.ArmatureAnimation.create();
655  */
656 ccs.ArmatureAnimation.create = function (armature) {
657     var animation = new ccs.ArmatureAnimation();
658     if (animation && animation.init(armature)) {
659         return animation;
660     }
661     return null;
662 };