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