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 
 68         var node = ccui.helper.seekActionWidgetByActionTag(root, dic["ActionTag"]);
 69         var positionOffset = node instanceof ccui.Widget && !(node instanceof ccui.Layout);
 70 
 71         for (var i = 0; i < actionFrameList.length; i++) {
 72             var actionFrameDic = actionFrameList[i];
 73             var frameIndex = actionFrameDic["frameid"];
 74             var frameTweenType = actionFrameDic["tweenType"];
 75             if(frameTweenType == null)
 76                 frameTweenType = 0;
 77             var frameTweenParameterNum = actionFrameDic["tweenParameter"];
 78 
 79             var frameTweenParameter = [];
 80             for (var j = 0; j < frameTweenParameterNum; j++){
 81                 var value = actionFrameDic["tweenParameter"][j];
 82                 frameTweenParameter.push(value);
 83             }
 84 
 85             var actionFrame, actionArray;
 86             if (actionFrameDic["positionx"] !== undefined) {
 87                 var positionX = actionFrameDic["positionx"];
 88                 var positionY = actionFrameDic["positiony"];
 89                 if(positionOffset && node.parent){
 90                     var AnchorPointIn = node.parent.getAnchorPointInPoints();
 91                     positionX += AnchorPointIn.x;
 92                     positionY += AnchorPointIn.y;
 93                 }
 94                 actionFrame = new ccs.ActionMoveFrame();
 95                 actionFrame.frameIndex = frameIndex;
 96                 actionFrame.setEasingType(frameTweenType);
 97                 actionFrame.setEasingParameter(frameTweenParameter);
 98                 actionFrame.setPosition(positionX, positionY);
 99                 actionArray = this._frameArray[ccs.FRAME_TYPE_MOVE];
100                 actionArray.push(actionFrame);
101             }
102 
103             if (actionFrameDic["scalex"] !== undefined) {
104                 var scaleX = actionFrameDic["scalex"];
105                 var scaleY = actionFrameDic["scaley"];
106                 actionFrame = new ccs.ActionScaleFrame();
107                 actionFrame.frameIndex = frameIndex;
108                 actionFrame.setEasingType(frameTweenType);
109                 actionFrame.setEasingParameter(frameTweenParameter);
110                 actionFrame.setScaleX(scaleX);
111                 actionFrame.setScaleY(scaleY);
112                 actionArray = this._frameArray[ccs.FRAME_TYPE_SCALE];
113                 actionArray.push(actionFrame);
114             }
115 
116             if (actionFrameDic["rotation"] !== undefined) {
117                 var rotation = actionFrameDic["rotation"];
118                 actionFrame = new ccs.ActionRotationFrame();
119                 actionFrame.frameIndex = frameIndex;
120                 actionFrame.setEasingType(frameTweenType);
121                 actionFrame.setEasingParameter(frameTweenParameter);
122                 actionFrame.setRotation(rotation);
123                 actionArray = this._frameArray[ccs.FRAME_TYPE_ROTATE];
124                 actionArray.push(actionFrame);
125             }
126 
127             if (actionFrameDic["opacity"] !== undefined) {
128                 var opacity = actionFrameDic["opacity"];
129                 actionFrame = new ccs.ActionFadeFrame();
130                 actionFrame.frameIndex = frameIndex;
131                 actionFrame.setEasingType(frameTweenType);
132                 actionFrame.setEasingParameter(frameTweenParameter);
133                 actionFrame.setOpacity(opacity);
134                 actionArray = this._frameArray[ccs.FRAME_TYPE_FADE];
135                 actionArray.push(actionFrame);
136             }
137 
138             if (actionFrameDic["colorr"] !== undefined) {
139                 var colorR = actionFrameDic["colorr"];
140                 var colorG = actionFrameDic["colorg"];
141                 var colorB = actionFrameDic["colorb"];
142                 actionFrame = new ccs.ActionTintFrame();
143                 actionFrame.frameIndex = frameIndex;
144                 actionFrame.setEasingType(frameTweenType);
145                 actionFrame.setEasingParameter(frameTweenParameter);
146                 actionFrame.setColor(cc.color(colorR, colorG, colorB));
147                 actionArray = this._frameArray[ccs.FRAME_TYPE_TINT];
148                 actionArray.push(actionFrame);
149             }
150             actionFrameDic = null;
151         }
152         this._initActionNodeFromRoot(root);
153     },
154 
155     _initActionNodeFromRoot: function (root) {
156         if (root instanceof ccui.Widget) {
157             var widget = ccui.helper.seekActionWidgetByActionTag(root, this.getActionTag());
158             if (widget)
159                 this.setObject(widget);
160         }
161     },
162 
163     /**
164      * Sets the time interval of frame.
165      * @param {number} time
166      */
167     setUnitTime: function (time) {
168         this._unitTime = time;
169         this._refreshActionProperty();
170     },
171 
172     /**
173      * Returns the time interval of frame.
174      * @returns {number}
175      */
176     getUnitTime: function () {
177         return this._unitTime;
178     },
179 
180     /**
181      * Sets tag to ccs.ActionNode
182      * @param {Number} tag
183      */
184     setActionTag: function (tag) {
185         this._actionTag = tag;
186     },
187 
188     /**
189      * Returns the tag of ccs.ActionNode
190      * @returns {number}
191      */
192     getActionTag: function () {
193         return this._actionTag;
194     },
195 
196     /**
197      * Sets node which will run a action.
198      * @param {Object} node
199      */
200     setObject: function (node) {
201         this._object = node;
202     },
203 
204     /**
205      * Returns node which will run a action.
206      * @returns {*}
207      */
208     getObject: function () {
209         return this._object;
210     },
211 
212     /**
213      * Returns the target node of ccs.ActionNode
214      * @returns {cc.Node}
215      */
216     getActionNode: function () {
217         if (this._object instanceof cc.Node)
218             return this._object;
219         return null;
220     },
221 
222     /**
223      * Inserts an ActionFrame to ccs.ActionNode.
224      * @param {number} index
225      * @param {ccs.ActionFrame} frame
226      */
227     insertFrame: function (index, frame) {
228         if (frame == null)
229             return;
230         var frameType = frame.frameType;
231         var array = this._frameArray[frameType];
232         array.splice(index, 0, frame);
233     },
234 
235     /**
236      * Pushes back an ActionFrame to ccs.ActionNode.
237      * @param {ccs.ActionFrame} frame
238      */
239     addFrame: function (frame) {
240         if (!frame)
241             return;
242         var frameType = frame.frameType;
243         var array = this._frameArray[frameType];
244         array.push(frame);
245     },
246 
247     /**
248      * Removes an ActionFrame from ccs.ActionNode.
249      * @param {ccs.ActionFrame} frame
250      */
251     deleteFrame: function (frame) {
252         if (frame == null)
253             return;
254         var frameType = frame.frameType;
255         var array = this._frameArray[frameType];
256         cc.arrayRemoveObject(array, frame);
257     },
258 
259     /**
260      * Removes all ActionFrames from ccs.ActionNode.
261      */
262     clearAllFrame: function () {
263         for (var i = 0; i < this._frameArrayNum; i++)
264             this._frameArray[i].length = 0;
265     },
266 
267     _refreshActionProperty: function () {
268         if (this._object === null)
269             return null;
270         var locSpawnArray = [];
271         for (var i = 0; i < this._frameArrayNum; i++) {
272             var locArray = this._frameArray[i];
273             if (locArray.length <= 0)
274                 continue;
275             var locSequenceArray = [];
276             for (var j = 0; j < locArray.length; j++) {
277                 var locFrame = locArray[j];
278                 var locAction = null;
279                 if (j !== 0) {
280                     var locSrcFrame = locArray[j - 1];
281                     var locDuration = (locFrame.frameIndex - locSrcFrame.frameIndex) * this.getUnitTime();
282                     locAction = locFrame.getAction(locDuration);
283                 }
284                 else {
285                     locAction = locFrame.getAction(0);
286                 }
287                 if(locAction)
288                     locSequenceArray.push(locAction);
289             }
290             if(locSequenceArray){
291                 var locSequence = cc.sequence(locSequenceArray);
292                 if (locSequence !== null)
293                     locSpawnArray.push(locSequence);
294             }
295         }
296 
297         this._action = null;
298         this._actionSpawn = cc.spawn(locSpawnArray);
299         return this._actionSpawn;
300     },
301 
302     /**
303      * Plays ccs.ActionNode's action.
304      * @param {cc.CallFunc} fun
305      */
306     playAction: function (fun) {
307         if (this._object === null || this._actionSpawn === null)
308             return;
309         if(fun)
310             this._action = cc.sequence(this._actionSpawn, fun);
311         else
312             this._action = cc.sequence(this._actionSpawn);
313         this._runAction();
314     },
315 
316     _runAction: function () {
317         var node = this.getActionNode();
318         if (node !== null && this._action !== null)
319             node.runAction(this._action);
320     },
321 
322     /**
323      * Stops action.
324      */
325     stopAction: function () {
326         var node = this.getActionNode();
327         if (node !== null && this._action !== null) {
328             if(!this._action.isDone())
329                 node.stopAction(this._action);
330         }
331     },
332 
333     /**
334      * Returns index of first ActionFrame.
335      * @returns {number}
336      */
337     getFirstFrameIndex: function () {
338         var locFrameindex = 99999;
339         var bFindFrame = false, locFrameArray = this._frameArray;
340         for (var i = 0, len = this._frameArrayNum; i < len; i++) {
341             var locArray = locFrameArray[i];
342             if (locArray.length <= 0)
343                 continue;
344             bFindFrame = true;
345             var locFrameIndex = locArray[0].frameIndex;
346             locFrameindex = locFrameindex > locFrameIndex ? locFrameIndex : locFrameindex;
347         }
348         if (!bFindFrame)
349             locFrameindex = 0;
350         return locFrameindex;
351     },
352 
353     /**
354      * Returns the index of last ccs.ActionFrame.
355      * @returns {number}
356      */
357     getLastFrameIndex: function () {
358         var locFrameindex = -1;
359         var locIsFindFrame = false ,locFrameArray = this._frameArray;
360         for (var i = 0, len = this._frameArrayNum; i < len; i++) {
361             var locArray = locFrameArray[i];
362             if (locArray.length <= 0)
363                 continue;
364             locIsFindFrame = true;
365             var locFrame = locArray[locArray.length - 1];
366             var locFrameIndex = locFrame.frameIndex;
367             locFrameindex = locFrameindex < locFrameIndex ? locFrameIndex : locFrameindex;
368         }
369         if (!locIsFindFrame)
370             locFrameindex = 0;
371         return locFrameindex;
372     },
373 
374     /**
375      * Updates action states to some time.
376      * @param {Number} time
377      * @returns {boolean}
378      */
379     updateActionToTimeLine: function (time) {
380         var locIsFindFrame = false;
381         var locUnitTime = this.getUnitTime();
382         for (var i = 0; i < this._frameArrayNum; i++) {
383             var locArray = this._frameArray[i];
384             if (locArray === null)
385                 continue;
386 
387             for (var j = 0; j < locArray.length; j++) {
388                 var locFrame = locArray[j];
389                 if (locFrame.frameIndex * locUnitTime === time) {
390                     this._easingToFrame(1.0, 1.0, locFrame);
391                     locIsFindFrame = true;
392                     break;
393                 } else if (locFrame.frameIndex * locUnitTime > time) {
394                     if (j === 0) {
395                         this._easingToFrame(1.0, 1.0, locFrame);
396                         locIsFindFrame = false;
397                     } else {
398                         var locSrcFrame = locArray[j - 1];
399                         var locDuration = (locFrame.frameIndex - locSrcFrame.frameIndex) * locUnitTime;
400                         var locDelaytime = time - locSrcFrame.frameIndex * locUnitTime;
401                         this._easingToFrame(locDuration, 1.0, locSrcFrame);
402                         this._easingToFrame(locDuration, locDelaytime / locDuration, locFrame);
403                         locIsFindFrame = true;
404                     }
405                     break;
406                 }
407             }
408         }
409         return locIsFindFrame;
410     },
411 
412     _easingToFrame: function (duration, delayTime, destFrame) {
413         var action = destFrame.getAction(duration);
414         var node = this.getActionNode();
415         if (action == null || node == null)
416             return;
417         action.startWithTarget(node);
418         action.update(delayTime);
419     },
420 
421     /**
422      * Returns if the action is done once time.
423      * @returns {Boolean} that if the action is done once time
424      */
425     isActionDoneOnce: function () {
426         if (this._action === null)
427             return true;
428         return this._action.isDone();
429     }
430 });
431