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