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