1 /****************************************************************************
  2  Copyright (c) 2011-2012 cocos2d-x.org
  3  Copyright (c) 2013-2014 Chukong Technologies Inc.
  4 
  5  http://www.cocos2d-x.org
  6 
  7  Permission is hereby granted, free of charge, to any person obtaining a copy
  8  of this software and associated documentation files (the "Software"), to deal
  9  in the Software without restriction, including without limitation the rights
 10  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 11  copies of the Software, and to permit persons to whom the Software is
 12  furnished to do so, subject to the following conditions:
 13 
 14  The above copyright notice and this permission notice shall be included in
 15  all copies or substantial portions of the Software.
 16 
 17  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 18  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 19  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 20  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 21  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 22  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 23  THE SOFTWARE.
 24  ****************************************************************************/
 25 
 26 /**
 27  * The Cocostudio's action node, it contains action target, action frame list and current frame index.  it can be play action by calling playAciton.
 28  * @class
 29  * @extends ccs.Class
 30  */
 31 ccs.ActionNode = ccs.Class.extend(/** @lends ccs.ActionNode# */{
 32     _currentFrameIndex: 0,
 33     _destFrameIndex: 0,
 34     _unitTime: 0,
 35     _actionTag: 0,
 36     _object: null,
 37     _actionSpawn: null,
 38     _action: null,
 39     _frameArray: null,
 40     _frameArrayNum: 0,
 41 
 42     /**
 43      * Construction of ccs.ActionNode
 44      */
 45     ctor: function () {
 46         this._currentFrameIndex = 0;
 47         this._destFrameIndex = 0;
 48         this._unitTime = 0.1;
 49         this._actionTag = 0;
 50         this._object = null;
 51         this._actionSpawn = null;
 52         this._action = null;
 53         this._frameArray = [];
 54         this._frameArrayNum = ccs.FRAME_TYPE_MAX;
 55         for (var i = 0; i < this._frameArrayNum; i++)
 56             this._frameArray.push([]);
 57     },
 58 
 59     /**
 60      *  Init properties with a json dictionary
 61      * @param {Object} dic
 62      * @param {Object} root
 63      */
 64     initWithDictionary: function (dic, root) {
 65         this.setActionTag(dic["ActionTag"]);
 66         var actionFrameList = dic["actionframelist"];
 67         for (var i = 0; i < actionFrameList.length; i++) {
 68             var actionFrameDic = actionFrameList[i];
 69             var frameIndex = actionFrameDic["frameid"];
 70             var frameTweenType = actionFrameDic["tweenType"];
 71             if(frameTweenType == null)
 72                 frameTweenType = 0;
 73             var frameTweenParameterNum = actionFrameDic["tweenParameter"];
 74 
 75             var frameTweenParameter = [];
 76             for (var j = 0; j < frameTweenParameterNum; j++){
 77                 var value = actionFrameDic["tweenParameter"][j];
 78                 frameTweenParameter.push(value);
 79             }
 80 
 81             var actionFrame, actionArray;
 82             if (actionFrameDic["positionx"] !== undefined) {
 83                 var positionX = actionFrameDic["positionx"];
 84                 var positionY = actionFrameDic["positiony"];
 85                 actionFrame = new ccs.ActionMoveFrame();
 86                 actionFrame.frameIndex = frameIndex;
 87                 actionFrame.setEasingType(frameTweenType);
 88                 actionFrame.setEasingParameter(frameTweenParameter);
 89                 actionFrame.setPosition(positionX, positionY);
 90                 actionArray = this._frameArray[ccs.FRAME_TYPE_MOVE];
 91                 actionArray.push(actionFrame);
 92             }
 93 
 94             if (actionFrameDic["scalex"] !== undefined) {
 95                 var scaleX = actionFrameDic["scalex"];
 96                 var scaleY = actionFrameDic["scaley"];
 97                 actionFrame = new ccs.ActionScaleFrame();
 98                 actionFrame.frameIndex = frameIndex;
 99                 actionFrame.setEasingType(frameTweenType);
100                 actionFrame.setEasingParameter(frameTweenParameter);
101                 actionFrame.setScaleX(scaleX);
102                 actionFrame.setScaleY(scaleY);
103                 actionArray = this._frameArray[ccs.FRAME_TYPE_SCALE];
104                 actionArray.push(actionFrame);
105             }
106 
107             if (actionFrameDic["rotation"] !== undefined) {
108                 var rotation = actionFrameDic["rotation"];
109                 actionFrame = new ccs.ActionRotationFrame();
110                 actionFrame.frameIndex = frameIndex;
111                 actionFrame.setEasingType(frameTweenType);
112                 actionFrame.setEasingParameter(frameTweenParameter);
113                 actionFrame.setRotation(rotation);
114                 actionArray = this._frameArray[ccs.FRAME_TYPE_ROTATE];
115                 actionArray.push(actionFrame);
116             }
117 
118             if (actionFrameDic["opacity"] !== undefined) {
119                 var opacity = actionFrameDic["opacity"];
120                 actionFrame = new ccs.ActionFadeFrame();
121                 actionFrame.frameIndex = frameIndex;
122                 actionFrame.setEasingType(frameTweenType);
123                 actionFrame.setEasingParameter(frameTweenParameter);
124                 actionFrame.setOpacity(opacity);
125                 actionArray = this._frameArray[ccs.FRAME_TYPE_FADE];
126                 actionArray.push(actionFrame);
127             }
128 
129             if (actionFrameDic["colorr"] !== undefined) {
130                 var colorR = actionFrameDic["colorr"];
131                 var colorG = actionFrameDic["colorg"];
132                 var colorB = actionFrameDic["colorb"];
133                 actionFrame = new ccs.ActionTintFrame();
134                 actionFrame.frameIndex = frameIndex;
135                 actionFrame.setEasingType(frameTweenType);
136                 actionFrame.setEasingParameter(frameTweenParameter);
137                 actionFrame.setColor(cc.color(colorR, colorG, colorB));
138                 actionArray = this._frameArray[ccs.FRAME_TYPE_TINT];
139                 actionArray.push(actionFrame);
140             }
141             actionFrameDic = null;
142         }
143         this._initActionNodeFromRoot(root);
144     },
145 
146     _initActionNodeFromRoot: function (root) {
147         if (root instanceof ccui.Widget) {
148             var widget = ccui.helper.seekActionWidgetByActionTag(root, this.getActionTag());
149             if (widget)
150                 this.setObject(widget);
151         }
152     },
153 
154     /**
155      * Sets the time interval of frame.
156      * @param {number} time
157      */
158     setUnitTime: function (time) {
159         this._unitTime = time;
160         this._refreshActionProperty();
161     },
162 
163     /**
164      * Returns the time interval of frame.
165      * @returns {number}
166      */
167     getUnitTime: function () {
168         return this._unitTime;
169     },
170 
171     /**
172      * Sets tag to ccs.ActionNode
173      * @param {Number} tag
174      */
175     setActionTag: function (tag) {
176         this._actionTag = tag;
177     },
178 
179     /**
180      * Returns the tag of ccs.ActionNode
181      * @returns {number}
182      */
183     getActionTag: function () {
184         return this._actionTag;
185     },
186 
187     /**
188      * Sets node which will run a action.
189      * @param {Object} node
190      */
191     setObject: function (node) {
192         this._object = node;
193     },
194 
195     /**
196      * Returns node which will run a action.
197      * @returns {*}
198      */
199     getObject: function () {
200         return this._object;
201     },
202 
203     /**
204      * Returns the target node of ccs.ActionNode
205      * @returns {cc.Node}
206      */
207     getActionNode: function () {
208         if (this._object instanceof cc.Node)
209             return this._object;
210         return null;
211     },
212 
213     /**
214      * Inserts an ActionFrame to ccs.ActionNode.
215      * @param {number} index
216      * @param {ccs.ActionFrame} frame
217      */
218     insertFrame: function (index, frame) {
219         if (frame == null)
220             return;
221         var frameType = frame.frameType;
222         var array = this._frameArray[frameType];
223         array.splice(index, 0, frame);
224     },
225 
226     /**
227      * Pushes back an ActionFrame to ccs.ActionNode.
228      * @param {ccs.ActionFrame} frame
229      */
230     addFrame: function (frame) {
231         if (!frame)
232             return;
233         var frameType = frame.frameType;
234         var array = this._frameArray[frameType];
235         array.push(frame);
236     },
237 
238     /**
239      * Removes an ActionFrame from ccs.ActionNode.
240      * @param {ccs.ActionFrame} frame
241      */
242     deleteFrame: function (frame) {
243         if (frame == null)
244             return;
245         var frameType = frame.frameType;
246         var array = this._frameArray[frameType];
247         cc.arrayRemoveObject(array, frame);
248     },
249 
250     /**
251      * Removes all ActionFrames from ccs.ActionNode.
252      */
253     clearAllFrame: function () {
254         for (var i = 0; i < this._frameArrayNum; i++)
255             this._frameArray[i].length = 0;
256     },
257 
258     _refreshActionProperty: function () {
259         if (this._object === null)
260             return null;
261         var locSpawnArray = [];
262         for (var i = 0; i < this._frameArrayNum; i++) {
263             var locArray = this._frameArray[i];
264             if (locArray.length <= 0)
265                 continue;
266             var locSequenceArray = [];
267             for (var j = 0; j < locArray.length; j++) {
268                 var locFrame = locArray[j];
269                 var locAction = null;
270                 if (j !== 0) {
271                     var locSrcFrame = locArray[j - 1];
272                     var locDuration = (locFrame.frameIndex - locSrcFrame.frameIndex) * this.getUnitTime();
273                     locAction = locFrame.getAction(locDuration);
274                 }
275                 else {
276                     locAction = locFrame.getAction(0);
277                 }
278                 if(locAction)
279                     locSequenceArray.push(locAction);
280             }
281             if(locSequenceArray){
282                 var locSequence = cc.sequence(locSequenceArray);
283                 if (locSequence !== null)
284                     locSpawnArray.push(locSequence);
285             }
286         }
287 
288         this._action = null;
289         this._actionSpawn = cc.spawn(locSpawnArray);
290         return this._actionSpawn;
291     },
292 
293     /**
294      * Plays ccs.ActionNode's action.
295      * @param {cc.CallFunc} fun
296      */
297     playAction: function (fun) {
298         if (this._object === null || this._actionSpawn === null)
299             return;
300         if(fun)
301             this._action = cc.sequence(this._actionSpawn, fun);
302         else
303             this._action = cc.sequence(this._actionSpawn);
304         this._runAction();
305     },
306 
307     _runAction: function () {
308         var node = this.getActionNode();
309         if (node !== null && this._action !== null)
310             node.runAction(this._action);
311     },
312 
313     /**
314      * Stops action.
315      */
316     stopAction: function () {
317         var node = this.getActionNode();
318         if (node !== null && this._action !== null) {
319             if(!this._action.isDone())
320                 node.stopAction(this._action);
321         }
322     },
323 
324     /**
325      * Returns index of first ActionFrame.
326      * @returns {number}
327      */
328     getFirstFrameIndex: function () {
329         var locFrameindex = 99999;
330         var bFindFrame = false, locFrameArray = this._frameArray;
331         for (var i = 0, len = this._frameArrayNum; i < len; i++) {
332             var locArray = locFrameArray[i];
333             if (locArray.length <= 0)
334                 continue;
335             bFindFrame = true;
336             var locFrameIndex = locArray[0].frameIndex;
337             locFrameindex = locFrameindex > locFrameIndex ? locFrameIndex : locFrameindex;
338         }
339         if (!bFindFrame)
340             locFrameindex = 0;
341         return locFrameindex;
342     },
343 
344     /**
345      * Returns the index of last ccs.ActionFrame.
346      * @returns {number}
347      */
348     getLastFrameIndex: function () {
349         var locFrameindex = -1;
350         var locIsFindFrame = false ,locFrameArray = this._frameArray;
351         for (var i = 0, len = this._frameArrayNum; i < len; i++) {
352             var locArray = locFrameArray[i];
353             if (locArray.length <= 0)
354                 continue;
355             locIsFindFrame = true;
356             var locFrame = locArray[locArray.length - 1];
357             var locFrameIndex = locFrame.frameIndex;
358             locFrameindex = locFrameindex < locFrameIndex ? locFrameIndex : locFrameindex;
359         }
360         if (!locIsFindFrame)
361             locFrameindex = 0;
362         return locFrameindex;
363     },
364 
365     /**
366      * Updates action states to some time.
367      * @param {Number} time
368      * @returns {boolean}
369      */
370     updateActionToTimeLine: function (time) {
371         var locIsFindFrame = false;
372         var locUnitTime = this.getUnitTime();
373         for (var i = 0; i < this._frameArrayNum; i++) {
374             var locArray = this._frameArray[i];
375             if (locArray === null)
376                 continue;
377 
378             for (var j = 0; j < locArray.length; j++) {
379                 var locFrame = locArray[j];
380                 if (locFrame.frameIndex * locUnitTime === time) {
381                     this._easingToFrame(1.0, 1.0, locFrame);
382                     locIsFindFrame = true;
383                     break;
384                 } else if (locFrame.frameIndex * locUnitTime > time) {
385                     if (j === 0) {
386                         this._easingToFrame(1.0, 1.0, locFrame);
387                         locIsFindFrame = false;
388                     } else {
389                         var locSrcFrame = locArray[j - 1];
390                         var locDuration = (locFrame.frameIndex - locSrcFrame.frameIndex) * locUnitTime;
391                         var locDelaytime = time - locSrcFrame.frameIndex * locUnitTime;
392                         this._easingToFrame(locDuration, 1.0, locSrcFrame);
393                         this._easingToFrame(locDuration, locDelaytime / locDuration, locFrame);
394                         locIsFindFrame = true;
395                     }
396                     break;
397                 }
398             }
399         }
400         return locIsFindFrame;
401     },
402 
403     _easingToFrame: function (duration, delayTime, destFrame) {
404         var action = destFrame.getAction(duration);
405         var node = this.getActionNode();
406         if (action == null || node == null)
407             return;
408         action.startWithTarget(node);
409         action.update(delayTime);
410     },
411 
412     /**
413      * Returns if the action is done once time.
414      * @returns {Boolean} that if the action is done once time
415      */
416     isActionDoneOnce: function () {
417         if (this._action === null)
418             return true;
419         return this._action.isDone();
420     }
421 });
422