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     ctor:function () {
257         cc.Action.prototype.ctor.call(this);
258         this._speed = 0;
259         this._innerAction = null;
260     },
261 
262     /**
263      * @return {Number}
264      */
265     getSpeed:function () {
266         return this._speed;
267     },
268 
269     /** alter the speed of the inner function in runtime
270      * @param {Number} speed
271      */
272     setSpeed:function (speed) {
273         this._speed = speed;
274     },
275 
276     /** initializes the action
277      * @param {cc.ActionInterval} action
278      * @param {Number} speed
279      * @return {Boolean}
280      */
281     initWithAction:function (action, speed) {
282         if(!action)
283             throw "cc.Speed.initWithAction(): action must be non nil";
284 
285         this._innerAction = action;
286         this._speed = speed;
287         return true;
288     },
289 
290     /**
291      * returns a clone of action
292      * @returns {cc.Speed}
293      */
294     clone:function () {
295         var action = new cc.Speed();
296         action.initWithAction(this._innerAction.clone(), this._speed);
297         return action;
298     },
299 
300     /**
301      * @param {cc.Node} target
302      */
303     startWithTarget:function (target) {
304         cc.Action.prototype.startWithTarget.call(this, target);
305         this._innerAction.startWithTarget(target);
306     },
307 
308     /**
309      *  Stop the action
310      */
311     stop:function () {
312         this._innerAction.stop();
313         cc.Action.prototype.stop.call(this);
314     },
315 
316     /**
317      * @param {Number} dt
318      */
319     step:function (dt) {
320         this._innerAction.step(dt * this._speed);
321     },
322 
323     /**
324      * @return {Boolean}
325      */
326     isDone:function () {
327         return this._innerAction.isDone();
328     },
329 
330     /**
331      * @return {cc.ActionInterval}
332      */
333     reverse:function () {
334         return (cc.Speed.create(this._innerAction.reverse(), this._speed));
335     },
336 
337     /**
338      *
339      * @param {cc.ActionInterval} action
340      */
341     setInnerAction:function (action) {
342         if (this._innerAction != action) {
343             this._innerAction = action;
344         }
345     },
346 
347     /**
348      *
349      * @return {cc.ActionInterval}
350      */
351     getInnerAction:function () {
352         return this._innerAction;
353     }
354 });
355 /** creates the action
356  *
357  * @param {cc.ActionInterval} action
358  * @param {Number} speed
359  * @return {cc.Speed}
360  */
361 cc.Speed.create = function (action, speed) {
362     var ret = new cc.Speed();
363     if (ret && ret.initWithAction(action, speed))
364         return ret;
365     return null;
366 };
367 
368 /**
369  * cc.Follow is an action that "follows" a node.
370 
371  * @example
372  * //example
373  * //Instead of using cc.Camera as a "follower", use this action instead.
374  * layer.runAction(cc.Follow.actionWithTarget(hero));
375 
376  * @class
377  * @extends cc.Action
378  */
379 cc.Follow = cc.Action.extend(/** @lends cc.Follow# */{
380     // node to follow
381     _followedNode:null,
382     // whether camera should be limited to certain area
383     _boundarySet:false,
384     // if screen size is bigger than the boundary - update not needed
385     _boundaryFullyCovered:false,
386     // fast access to the screen dimensions
387     _halfScreenSize:null,
388     _fullScreenSize:null,
389 
390     /** world leftBoundary
391      * @Type {Number}
392      */
393     leftBoundary:0.0,
394     /** world rightBoundary
395      * @Type Number
396      */
397     rightBoundary:0.0,
398     /** world topBoundary
399      * @Type Number
400      */
401     topBoundary:0.0,
402     /** world bottomBoundary
403      * @Type {Number}
404      */
405     bottomBoundary:0.0,
406     _worldRect:null,
407 
408     ctor:function () {
409         cc.Action.prototype.ctor.call(this);
410         this._followedNode = null;
411         this._boundarySet = false;
412 
413         this._boundaryFullyCovered = false;
414         this._halfScreenSize = null;
415         this._fullScreenSize = null;
416 
417         this.leftBoundary = 0.0;
418         this.rightBoundary = 0.0;
419         this.topBoundary = 0.0;
420         this.bottomBoundary = 0.0;
421         this._worldRect = cc.rect(0, 0, 0, 0);
422     },
423 
424     clone:function () {
425         var action = new cc.Follow();
426         var locRect = this._worldRect;
427         var rect = new cc.Rect(locRect.x, locRect.y, locRect.width, locRect.height);
428         action.initWithTarget(this._followedNode, rect);
429         return action;
430     },
431 
432     /**
433      * @return {Boolean}
434      */
435     isBoundarySet:function () {
436         return this._boundarySet;
437     },
438 
439     /** alter behavior - turn on/off boundary
440      * @param {Boolean} value
441      */
442     setBoudarySet:function (value) {
443         this._boundarySet = value;
444     },
445 
446     /** initializes the action
447      * initializes the action with a set boundary
448      * @param {cc.Node} followedNode
449      * @param {cc.Rect} [rect=]
450      * @return {Boolean}
451      */
452     initWithTarget:function (followedNode, rect) {
453         if(!followedNode)
454             throw "cc.Follow.initWithAction(): followedNode must be non nil";
455 
456         rect = rect || cc.rect(0, 0, 0, 0);
457         this._followedNode = followedNode;
458         this._worldRect = rect;
459 
460         this._boundarySet = !cc._rectEqualToZero(rect);
461 
462         this._boundaryFullyCovered = false;
463 
464         var winSize = cc.director.getWinSize();
465         this._fullScreenSize = cc.p(winSize.width, winSize.height);
466         this._halfScreenSize = cc.pMult(this._fullScreenSize, 0.5);
467 
468         if (this._boundarySet) {
469             this.leftBoundary = -((rect.x + rect.width) - this._fullScreenSize.x);
470             this.rightBoundary = -rect.x;
471             this.topBoundary = -rect.y;
472             this.bottomBoundary = -((rect.y + rect.height) - this._fullScreenSize.y);
473 
474             if (this.rightBoundary < this.leftBoundary) {
475                 // screen width is larger than world's boundary width
476                 //set both in the middle of the world
477                 this.rightBoundary = this.leftBoundary = (this.leftBoundary + this.rightBoundary) / 2;
478             }
479             if (this.topBoundary < this.bottomBoundary) {
480                 // screen width is larger than world's boundary width
481                 //set both in the middle of the world
482                 this.topBoundary = this.bottomBoundary = (this.topBoundary + this.bottomBoundary) / 2;
483             }
484 
485             if ((this.topBoundary == this.bottomBoundary) && (this.leftBoundary == this.rightBoundary))
486                 this._boundaryFullyCovered = true;
487         }
488         return true;
489     },
490 
491     /**
492      * @param {Number} dt
493      */
494     step:function (dt) {
495         var tempPosX = this._followedNode.x;
496         var tempPosY = this._followedNode.y;
497         tempPosX = this._halfScreenSize.x - tempPosX;
498         tempPosY = this._halfScreenSize.y - tempPosY;
499 
500         if (this._boundarySet) {
501             // whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased
502             if (this._boundaryFullyCovered)
503                 return;
504 
505 	        this.target.setPosition(cc.clampf(tempPosX, this.leftBoundary, this.rightBoundary), cc.clampf(tempPosY, this.bottomBoundary, this.topBoundary));
506         } else {
507             this.target.setPosition(tempPosX, tempPosY);
508         }
509     },
510 
511     /**
512      * @return {Boolean}
513      */
514     isDone:function () {
515         return ( !this._followedNode.running );
516     },
517 
518     /**
519      * Stop the action.
520      */
521     stop:function () {
522         this.target = null;
523         cc.Action.prototype.stop.call(this);
524     }
525 });
526 /** creates the action with a set boundary <br/>
527  * creates the action with no boundary set
528  * @param {cc.Node} followedNode
529  * @param {cc.Rect} rect
530  * @return {cc.Follow|Null} returns the cc.Follow object on success
531  * @example
532  * // example
533  * // creates the action with a set boundary
534  * var sprite = cc.Sprite.create("spriteFileName");
535  * var followAction = cc.Follow.create(sprite, cc.rect(0, 0, s.width * 2 - 100, s.height));
536  * this.runAction(followAction);
537  *
538  * // creates the action with no boundary set
539  * var sprite = cc.Sprite.create("spriteFileName");
540  * var followAction = cc.Follow.create(sprite);
541  * this.runAction(followAction);
542  */
543 cc.Follow.create = function (followedNode, rect) {
544     rect = rect || cc.rect(0, 0, 0, 0);
545     var ret = new cc.Follow();
546     if (rect != null && ret && ret.initWithTarget(followedNode, rect))
547         return ret;
548     else if (ret && ret.initWithTarget(followedNode))
549         return ret;
550     return null;
551 };
552