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