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  * @returns {cc.Action}
184  * @example
185  * // example
186  * var action = cc.Action.create();
187  */
188 cc.Action.create = function () {
189     return new cc.Action();
190 };
191 
192 
193 /**
194  * <p>Base class actions that do have a finite time duration.<br/>
195  * Possible actions: <br/>
196  * - An action with a duration of 0 seconds<br/>
197  * - An action with a duration of 35.5 seconds  </p>
198 
199  * Infinite time actions are valid
200  * @class
201  * @extends cc.Action
202  */
203 cc.FiniteTimeAction = cc.Action.extend(/** @lends cc.FiniteTimeAction# */{
204     //! duration in seconds
205     _duration:0,
206 
207     ctor:function () {
208         cc.Action.prototype.ctor.call(this);
209         this._duration = 0;
210     },
211 
212     /** get duration in seconds of the action
213      *
214      * @return {Number}
215      */
216     getDuration:function () {
217         return this._duration * (this._times || 1);
218     },
219 
220     /** set duration in seconds of the action
221      *
222      * @param {Number} duration
223      */
224     setDuration:function (duration) {
225         this._duration = duration;
226     },
227 
228     /** returns a reversed action
229      *
230      * @return {Null}
231      */
232     reverse:function () {
233         cc.log("cocos2d: FiniteTimeAction#reverse: Implement me");
234         return null;
235     },
236 
237     /**
238      *
239      */
240     clone:function () {
241         return new cc.FiniteTimeAction();
242     }
243 });
244 
245 /**
246  * Changes the speed of an action, making it take longer (speed>1)
247  * or less (speed<1) time. <br/>
248  * Useful to simulate 'slow motion' or 'fast forward' effect.
249  * @warning This action can't be Sequenceable because it is not an cc.IntervalAction
250  * @class
251  * @extends cc.Action
252  */
253 cc.Speed = cc.Action.extend(/** @lends cc.Speed# */{
254     _speed:0.0,
255     _innerAction:null,
256 
257 	/**
258 	 * Constructor of cc.Speed
259 	 * @param {cc.ActionInterval} action
260 	 * @param {Number} speed
261 	 */
262     ctor:function (action, speed) {
263         cc.Action.prototype.ctor.call(this);
264         this._speed = 0;
265         this._innerAction = null;
266 
267 		action && this.initWithAction(action, speed);
268     },
269 
270     /**
271      * @return {Number}
272      */
273     getSpeed:function () {
274         return this._speed;
275     },
276 
277     /** alter the speed of the inner function in runtime
278      * @param {Number} speed
279      */
280     setSpeed:function (speed) {
281         this._speed = speed;
282     },
283 
284     /** initializes the action
285      * @param {cc.ActionInterval} action
286      * @param {Number} speed
287      * @return {Boolean}
288      */
289     initWithAction:function (action, speed) {
290         if(!action)
291             throw "cc.Speed.initWithAction(): action must be non nil";
292 
293         this._innerAction = action;
294         this._speed = speed;
295         return true;
296     },
297 
298     /**
299      * returns a clone of action
300      * @returns {cc.Speed}
301      */
302     clone:function () {
303         var action = new cc.Speed();
304         action.initWithAction(this._innerAction.clone(), this._speed);
305         return action;
306     },
307 
308     /**
309      * @param {cc.Node} target
310      */
311     startWithTarget:function (target) {
312         cc.Action.prototype.startWithTarget.call(this, target);
313         this._innerAction.startWithTarget(target);
314     },
315 
316     /**
317      *  Stop the action
318      */
319     stop:function () {
320         this._innerAction.stop();
321         cc.Action.prototype.stop.call(this);
322     },
323 
324     /**
325      * @param {Number} dt
326      */
327     step:function (dt) {
328         this._innerAction.step(dt * this._speed);
329     },
330 
331     /**
332      * @return {Boolean}
333      */
334     isDone:function () {
335         return this._innerAction.isDone();
336     },
337 
338     /**
339      * @return {cc.ActionInterval}
340      */
341     reverse:function () {
342         return (cc.Speed.create(this._innerAction.reverse(), this._speed));
343     },
344 
345     /**
346      *
347      * @param {cc.ActionInterval} action
348      */
349     setInnerAction:function (action) {
350         if (this._innerAction != action) {
351             this._innerAction = action;
352         }
353     },
354 
355     /**
356      *
357      * @return {cc.ActionInterval}
358      */
359     getInnerAction:function () {
360         return this._innerAction;
361     }
362 });
363 /** creates the action
364  *
365  * @param {cc.ActionInterval} action
366  * @param {Number} speed
367  * @return {cc.Speed}
368  */
369 cc.Speed.create = function (action, speed) {
370     return new cc.Speed(action, speed);
371 };
372 
373 /**
374  * cc.Follow is an action that "follows" a node.
375 
376  * @example
377  * //example
378  * //Instead of using cc.Camera as a "follower", use this action instead.
379  * layer.runAction(cc.Follow.actionWithTarget(hero));
380 
381  * @class
382  * @extends cc.Action
383  */
384 cc.Follow = cc.Action.extend(/** @lends cc.Follow# */{
385     // node to follow
386     _followedNode:null,
387     // whether camera should be limited to certain area
388     _boundarySet:false,
389     // if screen size is bigger than the boundary - update not needed
390     _boundaryFullyCovered:false,
391     // fast access to the screen dimensions
392     _halfScreenSize:null,
393     _fullScreenSize:null,
394 
395     /** world leftBoundary
396      * @Type {Number}
397      */
398     leftBoundary:0.0,
399     /** world rightBoundary
400      * @Type Number
401      */
402     rightBoundary:0.0,
403     /** world topBoundary
404      * @Type Number
405      */
406     topBoundary:0.0,
407     /** world bottomBoundary
408      * @Type {Number}
409      */
410     bottomBoundary:0.0,
411     _worldRect:null,
412 
413 	/**
414 	 * creates the action with a set boundary <br/>
415 	 * creates the action with no boundary set
416 	 *
417 	 * Constructor of cc.Follow
418 	 * @param {cc.Node} followedNode
419 	 * @param {cc.Rect} rect
420 	 * @example
421 	 * // example
422 	 * // creates the action with a set boundary
423 	 * var sprite = new cc.Sprite("spriteFileName");
424 	 * var followAction = new cc.Follow(sprite, cc.rect(0, 0, s.width * 2 - 100, s.height));
425 	 * this.runAction(followAction);
426 	 *
427 	 * // creates the action with no boundary set
428 	 * var sprite = new cc.Sprite("spriteFileName");
429 	 * var followAction = new cc.Follow(sprite);
430 	 * this.runAction(followAction);
431 	 */
432     ctor:function (followedNode, rect) {
433         cc.Action.prototype.ctor.call(this);
434         this._followedNode = null;
435         this._boundarySet = false;
436 
437         this._boundaryFullyCovered = false;
438         this._halfScreenSize = null;
439         this._fullScreenSize = null;
440 
441         this.leftBoundary = 0.0;
442         this.rightBoundary = 0.0;
443         this.topBoundary = 0.0;
444         this.bottomBoundary = 0.0;
445         this._worldRect = cc.rect(0, 0, 0, 0);
446 
447 		if(followedNode)
448 			rect ? this.initWithTarget(followedNode, rect)
449 				 : this.initWithTarget(followedNode);
450     },
451 
452     clone:function () {
453         var action = new cc.Follow();
454         var locRect = this._worldRect;
455         var rect = new cc.Rect(locRect.x, locRect.y, locRect.width, locRect.height);
456         action.initWithTarget(this._followedNode, rect);
457         return action;
458     },
459 
460     /**
461      * @return {Boolean}
462      */
463     isBoundarySet:function () {
464         return this._boundarySet;
465     },
466 
467     /** alter behavior - turn on/off boundary
468      * @param {Boolean} value
469      */
470     setBoudarySet:function (value) {
471         this._boundarySet = value;
472     },
473 
474     /** initializes the action
475      * initializes the action with a set boundary
476      * @param {cc.Node} followedNode
477      * @param {cc.Rect} [rect=]
478      * @return {Boolean}
479      */
480     initWithTarget:function (followedNode, rect) {
481         if(!followedNode)
482             throw "cc.Follow.initWithAction(): followedNode must be non nil";
483 
484         var _this = this;
485         rect = rect || cc.rect(0, 0, 0, 0);
486         _this._followedNode = followedNode;
487         _this._worldRect = rect;
488 
489         _this._boundarySet = !cc._rectEqualToZero(rect);
490 
491         _this._boundaryFullyCovered = false;
492 
493         var winSize = cc.director.getWinSize();
494         _this._fullScreenSize = cc.p(winSize.width, winSize.height);
495         _this._halfScreenSize = cc.pMult(_this._fullScreenSize, 0.5);
496 
497         if (_this._boundarySet) {
498             _this.leftBoundary = -((rect.x + rect.width) - _this._fullScreenSize.x);
499             _this.rightBoundary = -rect.x;
500             _this.topBoundary = -rect.y;
501             _this.bottomBoundary = -((rect.y + rect.height) - _this._fullScreenSize.y);
502 
503             if (_this.rightBoundary < _this.leftBoundary) {
504                 // screen width is larger than world's boundary width
505                 //set both in the middle of the world
506                 _this.rightBoundary = _this.leftBoundary = (_this.leftBoundary + _this.rightBoundary) / 2;
507             }
508             if (_this.topBoundary < _this.bottomBoundary) {
509                 // screen width is larger than world's boundary width
510                 //set both in the middle of the world
511                 _this.topBoundary = _this.bottomBoundary = (_this.topBoundary + _this.bottomBoundary) / 2;
512             }
513 
514             if ((_this.topBoundary == _this.bottomBoundary) && (_this.leftBoundary == _this.rightBoundary))
515                 _this._boundaryFullyCovered = true;
516         }
517         return true;
518     },
519 
520     /**
521      * @param {Number} dt
522      */
523     step:function (dt) {
524         var tempPosX = this._followedNode.x;
525         var tempPosY = this._followedNode.y;
526         tempPosX = this._halfScreenSize.x - tempPosX;
527         tempPosY = this._halfScreenSize.y - tempPosY;
528 
529         if (this._boundarySet) {
530             // whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased
531             if (this._boundaryFullyCovered)
532                 return;
533 
534 	        this.target.setPosition(cc.clampf(tempPosX, this.leftBoundary, this.rightBoundary), cc.clampf(tempPosY, this.bottomBoundary, this.topBoundary));
535         } else {
536             this.target.setPosition(tempPosX, tempPosY);
537         }
538     },
539 
540     /**
541      * @return {Boolean}
542      */
543     isDone:function () {
544         return ( !this._followedNode.running );
545     },
546 
547     /**
548      * Stop the action.
549      */
550     stop:function () {
551         this.target = null;
552         cc.Action.prototype.stop.call(this);
553     }
554 });
555 /** creates the action with a set boundary <br/>
556  * creates the action with no boundary set
557  * @param {cc.Node} followedNode
558  * @param {cc.Rect} rect
559  * @return {cc.Follow|Null} returns the cc.Follow object on success
560  * @example
561  * // example
562  * // creates the action with a set boundary
563  * var sprite = cc.Sprite.create("spriteFileName");
564  * var followAction = cc.Follow.create(sprite, cc.rect(0, 0, s.width * 2 - 100, s.height));
565  * this.runAction(followAction);
566  *
567  * // creates the action with no boundary set
568  * var sprite = cc.Sprite.create("spriteFileName");
569  * var followAction = cc.Follow.create(sprite);
570  * this.runAction(followAction);
571  */
572 cc.Follow.create = function (followedNode, rect) {
573     return new cc.Follow(followedNode, rect);
574 };
575