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 
 32 cc.ACTION_TAG_INVALID = -1;
 33 
 34 /**
 35  * Base class for cc.Action objects.
 36  * @class
 37  * @extends cc.Class
 38  */
 39 cc.Action = cc.Class.extend(/** @lends cc.Action# */{
 40     //***********variables*************
 41     _originalTarget:null,
 42 
 43     /** The "target".
 44      The target will be set with the 'startWithTarget' method.
 45      When the 'stop' method is called, target will be set to nil.
 46      The target is 'assigned', it is not 'retained'.
 47      */
 48     _target:null,
 49     _tag:cc.ACTION_TAG_INVALID,
 50 
 51     //**************Public Functions***********
 52     /**
 53      * @return {String}
 54      */
 55     description:function () {
 56         return "<cc.Action | Tag = " + this._tag + ">";
 57     },
 58 
 59     /**
 60      * to copy object with deep copy.
 61      * @param {object} zone
 62      * @return {object}
 63      */
 64     copyWithZone:function (zone) {
 65         return this.copy();
 66     },
 67 
 68     /**
 69      * to copy object with deep copy.
 70      * @return {object}
 71      */
 72     copy:function () {
 73         return cc.clone(this);
 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     /** get duration in seconds of the action
207      *
208      * @return {Number}
209      */
210     getDuration:function () {
211         return this._duration;
212     },
213 
214     /** set duration in seconds of the action
215      *
216      * @param {Number} duration
217      */
218     setDuration:function (duration) {
219         this._duration = duration;
220     },
221 
222     /** returns a reversed action
223      *
224      * @return {Null}
225      */
226     reverse:function () {
227         cc.log("cocos2d: FiniteTimeAction#reverse: Implement me");
228         return null;
229     }
230 });
231 
232 
233 /**
234  * Changes the speed of an action, making it take longer (speed>1)
235  * or less (speed<1) time. <br/>
236  * Useful to simulate 'slow motion' or 'fast forward' effect.
237  * @warning This action can't be Sequenceable because it is not an cc.IntervalAction
238  * @class
239  * @extends cc.Action
240  */
241 cc.Speed = cc.Action.extend(/** @lends cc.Speed# */{
242     _speed:0.0,
243     _innerAction:null,
244 
245     /**
246      * @return {Number}
247      */
248     getSpeed:function () {
249         return this._speed;
250     },
251 
252     /** alter the speed of the inner function in runtime
253      * @param {Number} speed
254      */
255     setSpeed:function (speed) {
256         this._speed = speed;
257     },
258 
259     /** initializes the action
260      * @param {cc.ActionInterval} action
261      * @param {Number} speed
262      * @return {Boolean}
263      */
264     initWithAction:function (action, speed) {
265         cc.Assert(action != null, "");
266         this._innerAction = action;
267         this._speed = speed;
268         return true;
269     },
270 
271     /**
272      * @param {cc.Node} target
273      */
274     startWithTarget:function (target) {
275         //this._super(target);
276         cc.Action.prototype.startWithTarget.call(this, target);
277         this._innerAction.startWithTarget(target);
278     },
279 
280     /**
281      *  Stop the action
282      */
283     stop:function () {
284         this._innerAction.stop();
285         cc.Action.prototype.stop.call(this);
286     },
287 
288     /**
289      * @param {Number} dt
290      */
291     step:function (dt) {
292         this._innerAction.step(dt * this._speed);
293     },
294 
295     /**
296      * @return {Boolean}
297      */
298     isDone:function () {
299         return this._innerAction.isDone();
300     },
301 
302     /**
303      * @return {cc.ActionInterval}
304      */
305     reverse:function () {
306         return (cc.Speed.create(this._innerAction.reverse(), this._speed));
307     },
308 
309     /**
310      *
311      * @param {cc.ActionInterval} action
312      */
313     setInnerAction:function (action) {
314         if (this._innerAction != action) {
315             this._innerAction = action;
316         }
317     },
318 
319     /**
320      *
321      * @return {cc.ActionInterval}
322      */
323     getInnerAction:function () {
324         return this._innerAction;
325     }
326 });
327 /** creates the action
328  *
329  * @param {cc.ActionInterval} action
330  * @param {Number} speed
331  * @return {cc.Speed}
332  */
333 cc.Speed.create = function (action, speed) {
334     var ret = new cc.Speed();
335     if (ret && ret.initWithAction(action, speed)) {
336         return ret;
337     }
338     return null;
339 };
340 
341 /**
342  * cc.Follow is an action that "follows" a node.
343 
344  * @example
345  * //example
346  * //Instead of using cc.Camera as a "follower", use this action instead.
347  * layer.runAction(cc.Follow.actionWithTarget(hero));
348 
349  * @class
350  * @extends cc.Action
351  */
352 cc.Follow = cc.Action.extend(/** @lends cc.Follow# */{
353     /**
354      * @return {Boolean}
355      */
356     isBoundarySet:function () {
357         return this._boundarySet;
358     },
359 
360     /** alter behavior - turn on/off boundary
361      * @param {Boolean} value
362      */
363     setBoudarySet:function (value) {
364         this._boundarySet = value;
365     },
366 
367     /** initializes the action
368      * initializes the action with a set boundary
369      * @param {cc.Node} followedNode
370      * @param {cc.Rect} rect
371      * @return {Boolean}
372      */
373     initWithTarget:function (followedNode, rect) {
374         cc.Assert(followedNode != null, "");
375 
376         rect = rect || cc.RectZero();
377         this._followedNode = followedNode;
378 
379         this._boundarySet = !cc.Rect.CCRectEqualToRect(rect, cc.RectZero());
380 
381         this._boundaryFullyCovered = false;
382 
383         var winSize = cc.Director.getInstance().getWinSize();
384         this._fullScreenSize = cc.p(winSize.width, winSize.height);
385         this._halfScreenSize = cc.pMult(this._fullScreenSize, 0.5);
386 
387         if (this._boundarySet) {
388             this.leftBoundary = -((rect.origin.x + rect.size.width) - this._fullScreenSize.x);
389             this.rightBoundary = -rect.origin.x;
390             this.topBoundary = -rect.origin.y;
391             this.bottomBoundary = -((rect.origin.y + rect.size.height) - this._fullScreenSize.y);
392 
393             if (this.rightBoundary < this.leftBoundary) {
394                 // screen width is larger than world's boundary width
395                 //set both in the middle of the world
396                 this.rightBoundary = this.leftBoundary = (this.leftBoundary + this.rightBoundary) / 2;
397             }
398             if (this.topBoundary < this.bottomBoundary) {
399                 // screen width is larger than world's boundary width
400                 //set both in the middle of the world
401                 this.topBoundary = this.bottomBoundary = (this.topBoundary + this.bottomBoundary) / 2;
402             }
403 
404             if ((this.topBoundary == this.bottomBoundary) && (this.leftBoundary == this.rightBoundary)) {
405                 this._boundaryFullyCovered = true;
406             }
407         }
408         return true;
409     },
410 
411     /**
412      * @param {Number} dt
413      */
414     step:function (dt) {
415         if (this._boundarySet) {
416             // whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased
417             if (this._boundaryFullyCovered)
418                 return;
419 
420             var tempPos = cc.pSub(this._halfScreenSize, this._followedNode.getPosition());
421 
422             this._target.setPosition(cc.p(cc.clampf(tempPos.x, this.leftBoundary, this.rightBoundary),
423                 cc.clampf(tempPos.y, this.bottomBoundary, this.topBoundary)));
424         } else {
425             this._target.setPosition(cc.pSub(this._halfScreenSize, this._followedNode.getPosition()));
426         }
427     },
428 
429     /**
430      * @return {Boolean}
431      */
432     isDone:function () {
433         return ( !this._followedNode.isRunning() );
434     },
435 
436     /**
437      * Stop the action.
438      */
439     stop:function () {
440         this._target = null;
441         cc.Action.prototype.stop.call(this);
442     },
443 
444     // node to follow
445     _followedNode:null,
446     // whether camera should be limited to certain area
447     _boundarySet:false,
448     // if screen size is bigger than the boundary - update not needed
449     _boundaryFullyCovered:false,
450     // fast access to the screen dimensions
451     _halfScreenSize:null,
452     _fullScreenSize:null,
453 
454     /** world leftBoundary
455      * @Type {Number}
456      */
457     leftBoundary:0.0,
458     /** world rightBoundary
459      * @Type Number
460      */
461     rightBoundary:0.0,
462     /** world topBoundary
463      * @Type Number
464      */
465     topBoundary:0.0,
466     /** world bottomBoundary
467      * @Type {Number}
468      */
469     bottomBoundary:0.0
470 });
471 /** creates the action with a set boundary <br/>
472  * creates the action with no boundary set
473  * @param {cc.Node} followedNode
474  * @param {cc.Rect} rect
475  * @return {cc.Follow|Null} returns the cc.Follow object on success
476  * @example
477  * // example
478  * // creates the action with a set boundary
479  * var sprite = cc.Sprite.create("spriteFileName");
480  * var followAction = cc.Follow.create(sprite, cc.rect(0, 0, s.width * 2 - 100, s.height));
481  * this.runAction(followAction);
482  *
483  * // creates the action with no boundary set
484  * var sprite = cc.Sprite.create("spriteFileName");
485  * var followAction = cc.Follow.create(sprite);
486  * this.runAction(followAction);
487  */
488 cc.Follow.create = function (followedNode, rect) {
489     rect = rect || new cc.RectZero();
490     var ret = new cc.Follow();
491     if (rect != null && ret && ret.initWithTarget(followedNode, rect)) {
492         return ret;
493     }
494     else if (ret && ret.initWithTarget(followedNode)) {
495         return ret;
496     }
497     return null;
498 };
499