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