1 /****************************************************************************
  2  Copyright (c) 2008-2010 Ricardo Quesada
  3  Copyright (c) 2011-2012 cocos2d-x.org
  4  Copyright (c) 2013-2014 Chukong Technologies 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 /** Default Action tag
 28  * @constant
 29  * @type {Number}
 30  */
 31 cc.ACTION_TAG_INVALID = -1;
 32 
 33 /**
 34  * Base class for cc.Action objects.
 35  * @class
 36  * @extends cc.Class
 37  *
 38  * @property {cc.Node}  target          - The target will be set with the 'startWithTarget' method. When the 'stop' method is called, target will be set to nil.
 39  * @property {cc.Node}  originalTarget  - The original target of the action.
 40  * @property {Number}   tag             - The tag of the action, can be used to find the action.
 41  */
 42 cc.Action = cc.Class.extend(/** @lends cc.Action# */{
 43     //***********variables*************
 44     originalTarget:null,
 45     target:null,
 46     tag:cc.ACTION_TAG_INVALID,
 47 
 48     //**************Public Functions***********
 49     ctor:function () {
 50         this.originalTarget = null;
 51         this.target = null;
 52         this.tag = cc.ACTION_TAG_INVALID;
 53     },
 54 
 55     /**
 56      * to copy object with deep copy.
 57      * @deprecated
 58      * @return {object}
 59      */
 60     copy:function () {
 61         cc.log("copy is deprecated. Please use clone instead.");
 62         return this.clone();
 63     },
 64 
 65     /**
 66      * returns a clone of action
 67      * @return {cc.Action}
 68      */
 69     clone:function () {
 70         var action = new cc.Action();
 71         action.originalTarget = null;
 72         action.target = null;
 73         action.tag = this.tag;
 74         return action;
 75     },
 76 
 77     /**
 78      * return true if the action has finished
 79      * @return {Boolean}
 80      */
 81     isDone:function () {
 82         return true;
 83     },
 84 
 85     /**
 86      * called before the action start. It will also set the target.
 87      * @param {cc.Node} target
 88      */
 89     startWithTarget:function (target) {
 90         this.originalTarget = target;
 91         this.target = target;
 92     },
 93 
 94     /**
 95      * called after the action has finished. It will set the 'target' to nil.
 96      * IMPORTANT: You should never call "action stop" manually. Instead, use: "target.stopAction(action);"
 97      */
 98     stop:function () {
 99         this.target = null;
100     },
101     /** called every frame with it's delta time. DON'T override unless you know what you are doing.
102      *
103      * @param {Number} dt
104      */
105 
106     step:function (dt) {
107         cc.log("[Action step]. override me");
108     },
109 
110     /**
111      <p>called once per frame. time a value between 0 and 1  </P>
112 
113      <p>For example:  <br/>
114      - 0 means that the action just started <br/>
115      - 0.5 means that the action is in the middle<br/>
116      - 1 means that the action is over </P>
117      * @param {Number}  time
118      */
119     update:function (time) {
120         cc.log("[Action update]. override me");
121     },
122 
123     /**
124      *
125      * @return {cc.Node}
126      */
127     getTarget:function () {
128         return this.target;
129     },
130 
131     /** The action will modify the target properties.
132      *
133      * @param {cc.Node} target
134      */
135     setTarget:function (target) {
136         this.target = target;
137     },
138 
139     /**
140      *
141      * @return {cc.Node}
142      */
143     getOriginalTarget:function () {
144         return this.originalTarget;
145     },
146 
147     /** Set the original target, since target can be nil. <br/>
148      * Is the target that were used to run the action.  <br/>
149      * Unless you are doing something complex, like cc.ActionManager, you should NOT call this method. <br/>
150      * The target is 'assigned', it is not 'retained'. <br/>
151      * @param {cc.Node} originalTarget
152      */
153     setOriginalTarget:function (originalTarget) {
154         this.originalTarget = originalTarget;
155     },
156 
157     /**
158      *
159      * @return {Number}
160      */
161     getTag:function () {
162         return this.tag;
163     },
164 
165     /**
166      *
167      * @param {Number} tag
168      */
169     setTag:function (tag) {
170         this.tag = tag;
171     },
172     /**
173      * Currently JavaScript Bindigns (JSB), in some cases, needs to use retain and release. This is a bug in JSB,
174      * and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB.
175      * This is a hack, and should be removed once JSB fixes the retain/release bug
176      */
177     retain:function () {
178     },
179     release:function () {
180     }
181 });
182 /** Allocates and initializes the action
183  * @function
184  * @returns {cc.Action}
185  * @example
186  * // example
187  * var action = cc.action();
188  */
189 cc.action = function () {
190     return new cc.Action();
191 };
192 /**
193  * Please use cc.action instead
194  * Allocates and initializes the action
195  * @returns {cc.Action}
196  * @example
197  * @static
198  * @deprecated
199  */
200 cc.Action.create = cc.action;
201 
202 
203 /**
204  * <p>Base class actions that do have a finite time duration.<br/>
205  * Possible actions: <br/>
206  * - An action with a duration of 0 seconds<br/>
207  * - An action with a duration of 35.5 seconds  </p>
208 
209  * Infinite time actions are valid
210  * @class
211  * @extends cc.Action
212  */
213 cc.FiniteTimeAction = cc.Action.extend(/** @lends cc.FiniteTimeAction# */{
214     //! duration in seconds
215     _duration:0,
216 
217     ctor:function () {
218         cc.Action.prototype.ctor.call(this);
219         this._duration = 0;
220     },
221 
222     /** get duration in seconds of the action
223      *
224      * @return {Number}
225      */
226     getDuration:function () {
227         return this._duration * (this._times || 1);
228     },
229 
230     /** set duration in seconds of the action
231      *
232      * @param {Number} duration
233      */
234     setDuration:function (duration) {
235         this._duration = duration;
236     },
237 
238     /** returns a reversed action
239      *
240      * @return {Null}
241      */
242     reverse:function () {
243         cc.log("cocos2d: FiniteTimeAction#reverse: Implement me");
244         return null;
245     },
246 
247     /**
248      *
249      */
250     clone:function () {
251         return new cc.FiniteTimeAction();
252     }
253 });
254 
255 /**
256  * Changes the speed of an action, making it take longer (speed>1)
257  * or less (speed<1) time. <br/>
258  * Useful to simulate 'slow motion' or 'fast forward' effect.
259  * @warning This action can't be Sequenceable because it is not an cc.IntervalAction
260  * @class
261  * @extends cc.Action
262  */
263 cc.Speed = cc.Action.extend(/** @lends cc.Speed# */{
264     _speed:0.0,
265     _innerAction:null,
266 
267 	/**
268 	 * Constructor of cc.Speed
269 	 * @param {cc.ActionInterval} action
270 	 * @param {Number} speed
271 	 */
272     ctor:function (action, speed) {
273         cc.Action.prototype.ctor.call(this);
274         this._speed = 0;
275         this._innerAction = null;
276 
277 		action && this.initWithAction(action, speed);
278     },
279 
280     /**
281      * @return {Number}
282      */
283     getSpeed:function () {
284         return this._speed;
285     },
286 
287     /** alter the speed of the inner function in runtime
288      * @param {Number} speed
289      */
290     setSpeed:function (speed) {
291         this._speed = speed;
292     },
293 
294     /** initializes the action
295      * @param {cc.ActionInterval} action
296      * @param {Number} speed
297      * @return {Boolean}
298      */
299     initWithAction:function (action, speed) {
300         if(!action)
301             throw "cc.Speed.initWithAction(): action must be non nil";
302 
303         this._innerAction = action;
304         this._speed = speed;
305         return true;
306     },
307 
308     /**
309      * returns a clone of action
310      * @returns {cc.Speed}
311      */
312     clone:function () {
313         var action = new cc.Speed();
314         action.initWithAction(this._innerAction.clone(), this._speed);
315         return action;
316     },
317 
318     /**
319      * @param {cc.Node} target
320      */
321     startWithTarget:function (target) {
322         cc.Action.prototype.startWithTarget.call(this, target);
323         this._innerAction.startWithTarget(target);
324     },
325 
326     /**
327      *  Stop the action
328      */
329     stop:function () {
330         this._innerAction.stop();
331         cc.Action.prototype.stop.call(this);
332     },
333 
334     /**
335      * @param {Number} dt
336      */
337     step:function (dt) {
338         this._innerAction.step(dt * this._speed);
339     },
340 
341     /**
342      * @return {Boolean}
343      */
344     isDone:function () {
345         return this._innerAction.isDone();
346     },
347 
348     /**
349      * @return {cc.ActionInterval}
350      */
351     reverse:function () {
352         return (cc.Speed.create(this._innerAction.reverse(), this._speed));
353     },
354 
355     /**
356      *
357      * @param {cc.ActionInterval} action
358      */
359     setInnerAction:function (action) {
360         if (this._innerAction != action) {
361             this._innerAction = action;
362         }
363     },
364 
365     /**
366      *
367      * @return {cc.ActionInterval}
368      */
369     getInnerAction:function () {
370         return this._innerAction;
371     }
372 });
373 /** creates the speed action
374  * @function
375  *
376  * @param {cc.ActionInterval} action
377  * @param {Number} speed
378  * @return {cc.Speed}
379  */
380 cc.speed = function (action, speed) {
381     return new cc.Speed(action, speed);
382 };
383 /**
384  * Please use cc.speed instead
385  * creates the action
386  *
387  * @param {cc.ActionInterval} action
388  * @param {Number} speed
389  * @return {cc.Speed}
390  * @static
391  * @deprecated
392  */
393 cc.Speed.create = cc.speed;
394 
395 /**
396  * cc.Follow is an action that "follows" a node.
397 
398  * @example
399  * //example
400  * //Instead of using cc.Camera as a "follower", use this action instead.
401  * layer.runAction(cc.follow(hero));
402 
403  * @class
404  * @extends cc.Action
405  */
406 cc.Follow = cc.Action.extend(/** @lends cc.Follow# */{
407     // node to follow
408     _followedNode:null,
409     // whether camera should be limited to certain area
410     _boundarySet:false,
411     // if screen size is bigger than the boundary - update not needed
412     _boundaryFullyCovered:false,
413     // fast access to the screen dimensions
414     _halfScreenSize:null,
415     _fullScreenSize:null,
416 
417     /** world leftBoundary
418      * @Type {Number}
419      */
420     leftBoundary:0.0,
421     /** world rightBoundary
422      * @Type Number
423      */
424     rightBoundary:0.0,
425     /** world topBoundary
426      * @Type Number
427      */
428     topBoundary:0.0,
429     /** world bottomBoundary
430      * @Type {Number}
431      */
432     bottomBoundary:0.0,
433     _worldRect:null,
434 
435 	/**
436 	 * creates the action with a set boundary <br/>
437 	 * creates the action with no boundary set
438 	 *
439 	 * Constructor of cc.Follow
440 	 * @param {cc.Node} followedNode
441 	 * @param {cc.Rect} rect
442 	 * @example
443 	 * // example
444 	 * // creates the action with a set boundary
445 	 * var sprite = new cc.Sprite("spriteFileName");
446 	 * var followAction = new cc.Follow(sprite, cc.rect(0, 0, s.width * 2 - 100, s.height));
447 	 * this.runAction(followAction);
448 	 *
449 	 * // creates the action with no boundary set
450 	 * var sprite = new cc.Sprite("spriteFileName");
451 	 * var followAction = new cc.Follow(sprite);
452 	 * this.runAction(followAction);
453 	 */
454     ctor:function (followedNode, rect) {
455         cc.Action.prototype.ctor.call(this);
456         this._followedNode = null;
457         this._boundarySet = false;
458 
459         this._boundaryFullyCovered = false;
460         this._halfScreenSize = null;
461         this._fullScreenSize = null;
462 
463         this.leftBoundary = 0.0;
464         this.rightBoundary = 0.0;
465         this.topBoundary = 0.0;
466         this.bottomBoundary = 0.0;
467         this._worldRect = cc.rect(0, 0, 0, 0);
468 
469 		if(followedNode)
470 			rect ? this.initWithTarget(followedNode, rect)
471 				 : this.initWithTarget(followedNode);
472     },
473 
474     clone:function () {
475         var action = new cc.Follow();
476         var locRect = this._worldRect;
477         var rect = new cc.Rect(locRect.x, locRect.y, locRect.width, locRect.height);
478         action.initWithTarget(this._followedNode, rect);
479         return action;
480     },
481 
482     /**
483      * @return {Boolean}
484      */
485     isBoundarySet:function () {
486         return this._boundarySet;
487     },
488 
489     /** alter behavior - turn on/off boundary
490      * @param {Boolean} value
491      */
492     setBoudarySet:function (value) {
493         this._boundarySet = value;
494     },
495 
496     /** initializes the action
497      * initializes the action with a set boundary
498      * @param {cc.Node} followedNode
499      * @param {cc.Rect} [rect=]
500      * @return {Boolean}
501      */
502     initWithTarget:function (followedNode, rect) {
503         if(!followedNode)
504             throw "cc.Follow.initWithAction(): followedNode must be non nil";
505 
506         var _this = this;
507         rect = rect || cc.rect(0, 0, 0, 0);
508         _this._followedNode = followedNode;
509         _this._worldRect = rect;
510 
511         _this._boundarySet = !cc._rectEqualToZero(rect);
512 
513         _this._boundaryFullyCovered = false;
514 
515         var winSize = cc.director.getWinSize();
516         _this._fullScreenSize = cc.p(winSize.width, winSize.height);
517         _this._halfScreenSize = cc.pMult(_this._fullScreenSize, 0.5);
518 
519         if (_this._boundarySet) {
520             _this.leftBoundary = -((rect.x + rect.width) - _this._fullScreenSize.x);
521             _this.rightBoundary = -rect.x;
522             _this.topBoundary = -rect.y;
523             _this.bottomBoundary = -((rect.y + rect.height) - _this._fullScreenSize.y);
524 
525             if (_this.rightBoundary < _this.leftBoundary) {
526                 // screen width is larger than world's boundary width
527                 //set both in the middle of the world
528                 _this.rightBoundary = _this.leftBoundary = (_this.leftBoundary + _this.rightBoundary) / 2;
529             }
530             if (_this.topBoundary < _this.bottomBoundary) {
531                 // screen width is larger than world's boundary width
532                 //set both in the middle of the world
533                 _this.topBoundary = _this.bottomBoundary = (_this.topBoundary + _this.bottomBoundary) / 2;
534             }
535 
536             if ((_this.topBoundary == _this.bottomBoundary) && (_this.leftBoundary == _this.rightBoundary))
537                 _this._boundaryFullyCovered = true;
538         }
539         return true;
540     },
541 
542     /**
543      * @param {Number} dt
544      */
545     step:function (dt) {
546         var tempPosX = this._followedNode.x;
547         var tempPosY = this._followedNode.y;
548         tempPosX = this._halfScreenSize.x - tempPosX;
549         tempPosY = this._halfScreenSize.y - tempPosY;
550 
551         if (this._boundarySet) {
552             // whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased
553             if (this._boundaryFullyCovered)
554                 return;
555 
556 	        this.target.setPosition(cc.clampf(tempPosX, this.leftBoundary, this.rightBoundary), cc.clampf(tempPosY, this.bottomBoundary, this.topBoundary));
557         } else {
558             this.target.setPosition(tempPosX, tempPosY);
559         }
560     },
561 
562     /**
563      * @return {Boolean}
564      */
565     isDone:function () {
566         return ( !this._followedNode.running );
567     },
568 
569     /**
570      * Stop the action.
571      */
572     stop:function () {
573         this.target = null;
574         cc.Action.prototype.stop.call(this);
575     }
576 });
577 /** creates the action with a set boundary <br/>
578  * creates the action with no boundary set
579  * @function
580  * @param {cc.Node} followedNode
581  * @param {cc.Rect} rect
582  * @return {cc.Follow|Null} returns the cc.Follow object on success
583  * @example
584  * // example
585  * // creates the action with a set boundary
586  * var sprite = cc.Sprite.create("spriteFileName");
587  * var followAction = cc.follow(sprite, cc.rect(0, 0, s.width * 2 - 100, s.height));
588  * this.runAction(followAction);
589  *
590  * // creates the action with no boundary set
591  * var sprite = cc.Sprite.create("spriteFileName");
592  * var followAction = cc.follow(sprite);
593  * this.runAction(followAction);
594  */
595 cc.follow = function (followedNode, rect) {
596     return new cc.Follow(followedNode, rect);
597 };
598 
599 /**
600  * Please use cc.follow instead
601  * creates the action with a set boundary <br/>
602  * creates the action with no boundary set
603  * @param {cc.Node} followedNode
604  * @param {cc.Rect} rect
605  * @return {cc.Follow|Null} returns the cc.Follow object on success
606  * @static
607  * @deprecated
608  */
609 cc.Follow.create = cc.follow;
610