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({
 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  * frame event
 59  * @constructor
 60  */
 61 ccs.FrameEvent = function () {
 62     this.bone = null;
 63     this.frameEventName = "";
 64     this.originFrameIndex = 0;
 65     this.currentFrameIndex = 0;
 66 };
 67 /**
 68  * Base class for ccs.ArmatureAnimation objects.
 69  * @class
 70  * @extends ccs.ProcessBase
 71  */
 72 ccs.ArmatureAnimation = ccs.ProcessBase.extend(/** @lends ccs.ArmatureAnimation# */{
 73     _animationData:null,
 74     _movementData:null,
 75     _armature:null,
 76     _movementID:"",
 77     _prevFrameIndex:0,
 78     _toIndex:0,
 79     _tweenList:null,
 80     _frameEvent:null,
 81     _movementEvent:null,
 82     _speedScale:1,
 83     _ignoreFrameEvent:false,
 84     _frameEventQueue:[],
 85     _userObject:null,
 86     ctor:function () {
 87         ccs.ProcessBase.prototype.ctor.call(this);
 88         this._animationData = null;
 89         this._movementData = null;
 90         this._movementID = "";
 91         this._armature = null;
 92         this._prevFrameIndex = 0;
 93         this._toIndex = 0;
 94         this._tweenList = [];
 95         this._frameEvent = null;
 96         this._movementEvent = null;
 97         this._speedScale = 1;
 98         this._ignoreFrameEvent = false;
 99         this._frameEventQueue = [];
100         this._userObject = null;
101     },
102 
103     /**
104      * init with a CCArmature
105      * @param {ccs.Armature} armature
106      * @return {Boolean}
107      */
108     init:function (armature) {
109         this._armature = armature;
110         this._tweenList = [];
111         return true;
112     },
113     pause:function () {
114         for (var i = 0; i < this._tweenList.length; i++) {
115             this._tweenList[i].pause();
116         }
117         ccs.ProcessBase.prototype.pause.call(this);
118     },
119     resume:function () {
120         for (var i = 0; i < this._tweenList.length; i++) {
121             this._tweenList[i].resume();
122         }
123         ccs.ProcessBase.prototype.resume.call(this);
124     },
125 
126     stop:function () {
127         for (var i = 0; i < this._tweenList.length; i++) {
128             this._tweenList[i].stop();
129         }
130         this._tweenList = [];
131         ccs.ProcessBase.prototype.stop.call(this);
132     },
133 
134     /**
135      * scale animation play speed
136      * @param {Number} speedScale
137      */
138     setSpeedScale:function (speedScale) {
139         if (speedScale == this._speedScale) {
140             return;
141         }
142         this._speedScale = speedScale;
143         this._processScale = !this._movementData ? this._speedScale : this._speedScale * this._movementData.scale;
144         var dict = this._armature.getBoneDic();
145         for (var key in dict) {
146             var bone = dict[key];
147             bone.getTween().setProcessScale(this._processScale);
148             if (bone.getChildArmature()) {
149                 bone.getChildArmature().getAnimation().setProcessScale(this._processScale);
150             }
151         }
152     },
153 
154     getSpeedScale:function(){
155         return this._speedScale;
156     },
157 
158     getAnimationScale:function(){
159         return this.getSpeedScale();
160     },
161     setAnimationScale:function(animationScale){
162         return this.setSpeedScale(animationScale);
163     },
164 
165     setAnimationInternal:function (animationInternal) {
166         if (animationInternal == this._animationInternal) {
167             return;
168         }
169         this._animationInternal = animationInternal;
170 
171         var dict = this._armature.getBoneDic();
172         for (var key in dict) {
173             var bone = dict[key];
174             bone.getTween().setAnimationInternal(this._animationInternal);
175             if (bone.getChildArmature()) {
176                 bone.getChildArmature().getAnimation().setAnimationInternal(this._animationInternal);
177             }
178         }
179     },
180 
181     /**
182      * play animation by animation name.
183      * @param {Number} animationName The animation name you want to play
184      * @param {Number} durationTo
185      *         he frames between two animation changing-over.It's meaning is changing to this animation need how many frames
186      *         -1 : use the value from CCMovementData get from flash design panel
187      * @param {Number} durationTween he
188      *         frame count you want to play in the game.if  _durationTween is 80, then the animation will played 80 frames in a loop
189      *         -1 : use the value from CCMovementData get from flash design panel
190      * @param {Number} loop
191      *          Whether the animation is loop.
192      *         loop < 0 : use the value from CCMovementData get from flash design panel
193      *         loop = 0 : this animation is not loop
194      *         loop > 0 : this animation is loop
195      * @param {Number} tweenEasing
196      *          CCTween easing is used for calculate easing effect
197      *         TWEEN_EASING_MAX : use the value from CCMovementData get from flash design panel
198      *         -1 : fade out
199      *         0  : line
200      *         1  : fade in
201      *         2  : fade in and out
202      */
203     play:function (animationName, durationTo, durationTween, loop, tweenEasing) {
204         if (this._animationData == null) {
205             cc.log("this._animationData can not be null");
206             return;
207         }
208         this._movementData = this._animationData.getMovement(animationName);
209         if (this._movementData == null) {
210             cc.log("this._movementData can not be null");
211             return;
212         }
213         if (typeof durationTo == "undefined") {
214             durationTo = -1;
215         }
216         if (typeof durationTween == "undefined") {
217             durationTween = -1;
218         }
219         if (typeof loop == "undefined") {
220             loop = -1;
221         }
222         if (typeof tweenEasing == "undefined") {
223             tweenEasing = ccs.TweenType.tweenEasingMax;
224         }
225         var locMovementData = this._movementData;
226         //Get key frame count
227         this._rawDuration = locMovementData.duration;
228         this._movementID = animationName;
229         this._processScale = this._speedScale * locMovementData.scale;
230         //Further processing parameters
231         durationTo = (durationTo == -1) ? locMovementData.durationTo : durationTo;
232         durationTween = (durationTween == -1) ? locMovementData.durationTween : durationTween;
233         durationTween = (durationTween == 0) ? locMovementData.duration : durationTween;//todo
234         tweenEasing = (tweenEasing == ccs.TweenType.tweenEasingMax) ? locMovementData.tweenEasing : tweenEasing;
235         loop = (loop < 0) ? locMovementData.loop : loop;
236 
237         ccs.ProcessBase.prototype.play.call(this, durationTo, durationTween, loop, tweenEasing);
238 
239         if (this._rawDuration == 0) {
240             this._loopType = CC_ANIMATION_TYPE_SINGLE_FRAME;
241         }
242         else {
243             if (loop) {
244                 this._loopType = CC_ANIMATION_TYPE_TO_LOOP_FRONT;
245             }
246             else {
247                 this._loopType = CC_ANIMATION_TYPE_NO_LOOP;
248                 this._rawDuration--;
249             }
250             this._durationTween = durationTween;
251         }
252 
253         this._tweenList = [];
254 
255         var movementBoneData;
256         var dict = this._armature.getBoneDic();
257         for (var key in dict) {
258             var bone = dict[key];
259             movementBoneData = locMovementData.getMovementBoneData(bone.getName());
260             var tween = bone.getTween();
261             if (movementBoneData && movementBoneData.frameList.length > 0) {
262                 this._tweenList.push(tween);
263                 movementBoneData.duration = locMovementData.duration;
264                 tween.play(movementBoneData, durationTo, durationTween, loop, tweenEasing);
265 
266                 tween.setProcessScale(this._processScale);
267                 tween.setAnimationInternal(this._animationInternal);
268                 if (bone.getChildArmature()) {
269                     bone.getChildArmature().getAnimation().setProcessScale(this._processScale);
270                     bone.getChildArmature().getAnimation().setAnimationInternal(this._animationInternal);
271                 }
272             } else {
273                 if (!bone.getIgnoreMovementBoneData()) {
274                     bone.getDisplayManager().changeDisplayByIndex(-1, false);
275                     tween.stop();
276                 }
277             }
278         }
279         this._armature.update(0);
280     },
281 
282     /**
283      * Go to specified frame and play current movement.
284      * You need first switch to the movement you want to play, then call this function.
285      *
286      * example : playByIndex(0);
287      *           gotoAndPlay(0);
288      *           playByIndex(1);
289      *           gotoAndPlay(0);
290      *           gotoAndPlay(15);
291      * @param {Number} frameIndex
292      */
293     gotoAndPlay: function (frameIndex) {
294         if (!this._movementData || frameIndex < 0 || frameIndex >= this._movementData.duration) {
295             cc.log("Please ensure you have played a movement, and the frameIndex is in the range.");
296             return;
297         }
298 
299         var ignoreFrameEvent = this._ignoreFrameEvent;
300         this._ignoreFrameEvent = true;
301         this._isPlaying = true;
302         this._isComplete = this._isPause = false;
303 
304         ccs.ProcessBase.prototype.gotoFrame.call(this, frameIndex);
305         this._currentPercent = this._curFrameIndex / this._movementData.duration;
306         this._currentFrame = this._nextFrameIndex * this._currentPercent;
307 
308         for (var i = 0; i < this._tweenList.length; i++) {
309             var tween = this._tweenList[i];
310             tween.gotoAndPlay(frameIndex);
311         }
312         this._armature.update(0);
313         this._ignoreFrameEvent = ignoreFrameEvent;
314     },
315 
316     /**
317      * Go to specified frame and pause current movement.
318      * @param {Number} frameIndex
319      */
320     gotoAndPause: function (frameIndex) {
321         this.gotoAndPlay(frameIndex);
322         this.pause();
323     },
324 
325     /**
326      * Play animation by index, the other param is the same to play.
327      * @param {Number} animationIndex
328      * @param {Number} durationTo
329      * @param {Number} durationTween
330      * @param {Number} loop
331      * @param {Number} tweenEasing
332      */
333     playByIndex:function (animationIndex, durationTo, durationTween, loop, tweenEasing) {
334         if (typeof durationTo == "undefined") {
335             durationTo = -1;
336         }
337         if (typeof durationTween == "undefined") {
338             durationTween = -1;
339         }
340         if (typeof loop == "undefined") {
341             loop = -1;
342         }
343         if (typeof tweenEasing == "undefined") {
344             tweenEasing = 10000;
345         }
346         var moveNames = this._animationData.movementNames;
347         if (animationIndex < -1 || animationIndex >= moveNames.length) {
348             return;
349         }
350         var animationName = moveNames[animationIndex];
351         this.play(animationName, durationTo, durationTween, loop, tweenEasing);
352     },
353 
354     /**
355      * get movement count
356      * @return {Number}
357      */
358     getMovementCount:function () {
359         return this._animationData.getMovementCount();
360     },
361 
362     update:function (dt) {
363         if(ccs.ProcessBase.prototype.update.call(this, dt)){
364             for (var i = 0; i < this._tweenList.length; i++) {
365                 this._tweenList[i].update(dt);
366             }
367         }
368         if (this._frameEventQueue.length > 0) {
369             for (var i = 0; i < this._frameEventQueue.length; i++) {
370                 var frameEvent = this._frameEventQueue[i];
371                 this._ignoreFrameEvent = true;
372                 this.callFrameEvent([frameEvent.bone, frameEvent.frameEventName, frameEvent.originFrameIndex, frameEvent.currentFrameIndex]);
373                 this._ignoreFrameEvent = false;
374             }
375             this._frameEventQueue = [];
376         }
377     },
378 
379     /**
380      * update will call this handler, you can handle your logic here
381      */
382     updateHandler:function () {
383         var locCurrentPercent = this._currentPercent;
384         if (locCurrentPercent >= 1) {
385             switch (this._loopType) {
386                 case CC_ANIMATION_TYPE_NO_LOOP:
387                     this._loopType = CC_ANIMATION_TYPE_MAX;
388                     this._currentFrame = (locCurrentPercent - 1) * this._nextFrameIndex;
389                     locCurrentPercent = this._currentFrame / this._durationTween;
390                     if (locCurrentPercent < 1.0) {
391                         this._nextFrameIndex = this._durationTween;
392                         this.callMovementEvent([this._armature, ccs.MovementEventType.start, this._movementID]);
393                         break;
394                     }
395                 case CC_ANIMATION_TYPE_MAX:
396                 case CC_ANIMATION_TYPE_SINGLE_FRAME:
397                     locCurrentPercent = 1;
398                     this._isComplete = true;
399                     this._isPlaying = false;
400                     this.callMovementEvent([this._armature, ccs.MovementEventType.complete, this._movementID]);
401                     break;
402                 case CC_ANIMATION_TYPE_TO_LOOP_FRONT:
403                     this._loopType = CC_ANIMATION_TYPE_LOOP_FRONT;
404                     locCurrentPercent = ccs.fmodf(locCurrentPercent, 1);
405                     this._currentFrame = this._nextFrameIndex == 0 ? 0 : ccs.fmodf(this._currentFrame, this._nextFrameIndex);
406                     this._nextFrameIndex = this._durationTween > 0 ? this._durationTween : 1;
407                     this.callMovementEvent([this, ccs.MovementEventType.start, this._movementID]);
408                     break;
409                 default:
410                     //locCurrentPercent = ccs.fmodf(locCurrentPercent, 1);
411                     this._currentFrame = ccs.fmodf(this._currentFrame, this._nextFrameIndex);
412                     this._toIndex = 0;
413                     this.callMovementEvent([this._armature, ccs.MovementEventType.loopComplete, this._movementID]);
414                     break;
415             }
416             this._currentPercent = locCurrentPercent;
417         }
418     },
419 
420     /**
421      * Get current movementID
422      * @returns {String}
423      */
424     getCurrentMovementID: function () {
425         if (this._isComplete)
426             return "";
427         return this._movementID;
428     },
429 
430     /**
431      * connect a event
432      * @param {Object} target
433      * @param {function} callFunc
434      */
435     setMovementEventCallFunc:function (callFunc, target) {
436         this._movementEvent = new ccs.AnimationEvent(target, callFunc);
437     },
438 
439     /**
440      * call event
441      * @param {Array} args
442      */
443     callMovementEvent:function (args) {
444         if (this._movementEvent) {
445             this._movementEvent.setArguments(args);
446             this._movementEvent.call();
447         }
448     },
449 
450     /**
451      * connect a event
452      * @param {Object} target
453      * @param {function} callFunc
454      */
455     setFrameEventCallFunc:function (callFunc, target) {
456         this._frameEvent = new ccs.AnimationEvent(target, callFunc);
457     },
458 
459     /**
460      * call event
461      * @param {Array} args
462      */
463     callFrameEvent:function (args) {
464         if (this._frameEvent) {
465             this._frameEvent.setArguments(args);
466             this._frameEvent.call();
467         }
468     },
469 
470     /**
471      * @param {ccs.Bone} bone
472      * @param {String} frameEventName
473      * @param {Number} originFrameIndex
474      * @param {Number} currentFrameIndex
475      */
476     frameEvent:function(bone, frameEventName,  originFrameIndex,  currentFrameIndex){
477         if (this._frameEvent)    {
478             var frameEvent = new ccs.FrameEvent();
479             frameEvent.bone = bone;
480             frameEvent.frameEventName = frameEventName;
481             frameEvent.originFrameIndex = originFrameIndex;
482             frameEvent.currentFrameIndex = currentFrameIndex;
483             this._frameEventQueue.push(frameEvent);
484         }
485     },
486     
487     /**
488      * animationData setter
489      * @param {ccs.AnimationData} aniData
490      */
491     setAnimationData:function (aniData) {
492         this._animationData = aniData;
493     },
494 
495     /**
496      * animationData getter
497      * @return {ccs.AnimationData}
498      */
499     getAnimationData:function () {
500         return this._animationData;
501     },
502     /**
503      * userObject setter
504      * @param {Object} aniData
505      */
506     setUserObject:function (userObject) {
507         this._userObject = userObject;
508     },
509 
510     /**
511      * userObject getter
512      * @return {Object}
513      */
514     getUserObject:function () {
515         return this._userObject;
516     },
517 
518     /**
519      * Determines if the frame event is ignore
520      * @returns {boolean}
521      */
522     isIgnoreFrameEvent:function(){
523         return this._ignoreFrameEvent;
524     },
525 
526     /**
527      * Sets whether the frame event is ignore
528      * @param {Boolean} bool
529      */
530     setIgnoreFrameEvent:function(bool){
531         this._ignoreFrameEvent = bool;
532     }
533 });
534 
535 /**
536  * allocates and initializes a ArmatureAnimation.
537  * @constructs
538  * @return {ccs.ArmatureAnimation}
539  * @example
540  * // example
541  * var animation = ccs.ArmatureAnimation.create();
542  */
543 ccs.ArmatureAnimation.create = function (armature) {
544     var animation = new ccs.ArmatureAnimation();
545     if (animation && animation.init(armature)) {
546         return animation;
547     }
548     return null;
549 };