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 cc.Action = cc.Class.extend(/** @lends cc.Action# */{ 39 //***********variables************* 40 _originalTarget:null, 41 42 /** The "target". 43 The target will be set with the 'startWithTarget' method. 44 When the 'stop' method is called, target will be set to nil. 45 The target is 'assigned', it is not 'retained'. 46 */ 47 _target:null, 48 _tag:cc.ACTION_TAG_INVALID, 49 50 //**************Public Functions*********** 51 ctor:function () { 52 this._originalTarget = null; 53 this._target = null; 54 this._tag = cc.ACTION_TAG_INVALID; 55 }, 56 /** 57 * @return {String} 58 */ 59 description:function () { 60 return "<cc.Action | Tag = " + this._tag + ">"; 61 }, 62 63 /** 64 * to copy object with deep copy. 65 * @deprecated 66 * @return {object} 67 */ 68 copy:function () { 69 return cc.clone(this); 70 }, 71 72 /** 73 * returns a clone of action 74 * @return {cc.Action} 75 */ 76 clone:function () { 77 var action = new cc.Action(); 78 action._originalTarget = null; 79 action._target = null; 80 action._tag = this._tag; 81 return action; 82 }, 83 84 /** 85 * return true if the action has finished 86 * @return {Boolean} 87 */ 88 isDone:function () { 89 return true; 90 }, 91 92 /** 93 * called before the action start. It will also set the target. 94 * @param {cc.Node} target 95 */ 96 startWithTarget:function (target) { 97 this._originalTarget = target; 98 this._target = target; 99 }, 100 101 /** 102 * called after the action has finished. It will set the 'target' to nil. 103 * IMPORTANT: You should never call "action stop" manually. Instead, use: "target.stopAction(action);" 104 */ 105 stop:function () { 106 this._target = null; 107 }, 108 /** called every frame with it's delta time. DON'T override unless you know what you are doing. 109 * 110 * @param {Number} dt 111 */ 112 113 step:function (dt) { 114 cc.log("[Action step]. override me"); 115 }, 116 117 /** 118 <p>called once per frame. time a value between 0 and 1 </P> 119 120 <p>For example: <br/> 121 - 0 means that the action just started <br/> 122 - 0.5 means that the action is in the middle<br/> 123 - 1 means that the action is over </P> 124 * @param {Number} time 125 */ 126 update:function (time) { 127 cc.log("[Action update]. override me"); 128 }, 129 130 /** 131 * 132 * @return {cc.Node} 133 */ 134 getTarget:function () { 135 return this._target; 136 }, 137 138 /** The action will modify the target properties. 139 * 140 * @param {cc.Node} target 141 */ 142 setTarget:function (target) { 143 this._target = target; 144 }, 145 146 /** 147 * 148 * @return {cc.Node} 149 */ 150 getOriginalTarget:function () { 151 return this._originalTarget; 152 }, 153 154 /** Set the original target, since target can be nil. <br/> 155 * Is the target that were used to run the action. <br/> 156 * Unless you are doing something complex, like cc.ActionManager, you should NOT call this method. <br/> 157 * The target is 'assigned', it is not 'retained'. <br/> 158 * @param {cc.Node} originalTarget 159 */ 160 setOriginalTarget:function (originalTarget) { 161 this._originalTarget = originalTarget; 162 }, 163 164 /** 165 * 166 * @return {Number} 167 */ 168 getTag:function () { 169 return this._tag; 170 }, 171 172 /** 173 * 174 * @param {Number} tag 175 */ 176 setTag:function (tag) { 177 this._tag = tag; 178 }, 179 /** 180 * Currently JavaScript Bindigns (JSB), in some cases, needs to use retain and release. This is a bug in JSB, 181 * and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB. 182 * This is a hack, and should be removed once JSB fixes the retain/release bug 183 */ 184 retain:function () { 185 }, 186 release:function () { 187 } 188 }); 189 /** Allocates and initializes the action 190 * @returns {cc.Action} 191 * @example 192 * // example 193 * var action = cc.Action.create(); 194 */ 195 cc.Action.create = function () { 196 return new cc.Action(); 197 }; 198 199 200 /** 201 * <p>Base class actions that do have a finite time duration.<br/> 202 * Possible actions: <br/> 203 * - An action with a duration of 0 seconds<br/> 204 * - An action with a duration of 35.5 seconds </p> 205 206 * Infinite time actions are valid 207 * @class 208 * @extends cc.Action 209 */ 210 cc.FiniteTimeAction = cc.Action.extend(/** @lends cc.FiniteTimeAction# */{ 211 //! duration in seconds 212 _duration:0, 213 214 ctor:function () { 215 cc.Action.prototype.ctor.call(this); 216 this._duration = 0; 217 }, 218 219 /** get duration in seconds of the action 220 * 221 * @return {Number} 222 */ 223 getDuration:function () { 224 return this._duration; 225 }, 226 227 /** set duration in seconds of the action 228 * 229 * @param {Number} duration 230 */ 231 setDuration:function (duration) { 232 this._duration = duration; 233 }, 234 235 /** returns a reversed action 236 * 237 * @return {Null} 238 */ 239 reverse:function () { 240 cc.log("cocos2d: FiniteTimeAction#reverse: Implement me"); 241 return null; 242 }, 243 244 /** 245 * 246 */ 247 clone:function () { 248 return new cc.FiniteTimeAction(); 249 } 250 }); 251 252 /** 253 * Changes the speed of an action, making it take longer (speed>1) 254 * or less (speed<1) time. <br/> 255 * Useful to simulate 'slow motion' or 'fast forward' effect. 256 * @warning This action can't be Sequenceable because it is not an cc.IntervalAction 257 * @class 258 * @extends cc.Action 259 */ 260 cc.Speed = cc.Action.extend(/** @lends cc.Speed# */{ 261 _speed:0.0, 262 _innerAction:null, 263 264 ctor:function () { 265 cc.Action.prototype.ctor.call(this); 266 this._speed = 0; 267 this._innerAction = null; 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 var ret = new cc.Speed(); 371 if (ret && ret.initWithAction(action, speed)) 372 return ret; 373 return null; 374 }; 375 376 /** 377 * cc.Follow is an action that "follows" a node. 378 379 * @example 380 * //example 381 * //Instead of using cc.Camera as a "follower", use this action instead. 382 * layer.runAction(cc.Follow.actionWithTarget(hero)); 383 384 * @class 385 * @extends cc.Action 386 */ 387 cc.Follow = cc.Action.extend(/** @lends cc.Follow# */{ 388 // node to follow 389 _followedNode:null, 390 // whether camera should be limited to certain area 391 _boundarySet:false, 392 // if screen size is bigger than the boundary - update not needed 393 _boundaryFullyCovered:false, 394 // fast access to the screen dimensions 395 _halfScreenSize:null, 396 _fullScreenSize:null, 397 398 /** world leftBoundary 399 * @Type {Number} 400 */ 401 leftBoundary:0.0, 402 /** world rightBoundary 403 * @Type Number 404 */ 405 rightBoundary:0.0, 406 /** world topBoundary 407 * @Type Number 408 */ 409 topBoundary:0.0, 410 /** world bottomBoundary 411 * @Type {Number} 412 */ 413 bottomBoundary:0.0, 414 _worldRect:null, 415 416 ctor:function () { 417 cc.Action.prototype.ctor.call(this); 418 this._followedNode = null; 419 this._boundarySet = false; 420 421 this._boundaryFullyCovered = false; 422 this._halfScreenSize = null; 423 this._fullScreenSize = null; 424 425 this.leftBoundary = 0.0; 426 this.rightBoundary = 0.0; 427 this.topBoundary = 0.0; 428 this.bottomBoundary = 0.0; 429 this._worldRect = cc.RectZero(); 430 }, 431 432 clone:function () { 433 var action = new cc.Follow(); 434 var locRect = this._worldRect; 435 var rect = new cc.Rect(locRect.x, locRect.y, locRect.width, locRect.height); 436 action.initWithTarget(this._followedNode, rect); 437 return action; 438 }, 439 440 /** 441 * @return {Boolean} 442 */ 443 isBoundarySet:function () { 444 return this._boundarySet; 445 }, 446 447 /** alter behavior - turn on/off boundary 448 * @param {Boolean} value 449 */ 450 setBoudarySet:function (value) { 451 this._boundarySet = value; 452 }, 453 454 /** initializes the action 455 * initializes the action with a set boundary 456 * @param {cc.Node} followedNode 457 * @param {cc.Rect} [rect=] 458 * @return {Boolean} 459 */ 460 initWithTarget:function (followedNode, rect) { 461 if(!followedNode) 462 throw "cc.Follow.initWithAction(): followedNode must be non nil"; 463 464 rect = rect || cc.RectZero(); 465 this._followedNode = followedNode; 466 this._worldRect = rect; 467 468 this._boundarySet = !cc._rectEqualToZero(rect); 469 470 this._boundaryFullyCovered = false; 471 472 var winSize = cc.Director.getInstance().getWinSize(); 473 this._fullScreenSize = cc.p(winSize.width, winSize.height); 474 this._halfScreenSize = cc.pMult(this._fullScreenSize, 0.5); 475 476 if (this._boundarySet) { 477 this.leftBoundary = -((rect.x + rect.width) - this._fullScreenSize.x); 478 this.rightBoundary = -rect.x; 479 this.topBoundary = -rect.y; 480 this.bottomBoundary = -((rect.y + rect.height) - this._fullScreenSize.y); 481 482 if (this.rightBoundary < this.leftBoundary) { 483 // screen width is larger than world's boundary width 484 //set both in the middle of the world 485 this.rightBoundary = this.leftBoundary = (this.leftBoundary + this.rightBoundary) / 2; 486 } 487 if (this.topBoundary < this.bottomBoundary) { 488 // screen width is larger than world's boundary width 489 //set both in the middle of the world 490 this.topBoundary = this.bottomBoundary = (this.topBoundary + this.bottomBoundary) / 2; 491 } 492 493 if ((this.topBoundary == this.bottomBoundary) && (this.leftBoundary == this.rightBoundary)) 494 this._boundaryFullyCovered = true; 495 } 496 return true; 497 }, 498 499 /** 500 * @param {Number} dt 501 */ 502 step:function (dt) { 503 var tempPosX = this._followedNode.getPositionX(); 504 var tempPosY = this._followedNode.getPositionY(); 505 tempPosX = this._halfScreenSize.x - tempPosX; 506 tempPosY = this._halfScreenSize.y - tempPosY; 507 508 if (this._boundarySet) { 509 // whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased 510 if (this._boundaryFullyCovered) 511 return; 512 513 this._target.setPosition(cc.clampf(tempPosX, this.leftBoundary, this.rightBoundary), 514 cc.clampf(tempPosY, this.bottomBoundary, this.topBoundary)); 515 } else { 516 this._target.setPosition(tempPosX, tempPosY); 517 } 518 }, 519 520 /** 521 * @return {Boolean} 522 */ 523 isDone:function () { 524 return ( !this._followedNode.isRunning() ); 525 }, 526 527 /** 528 * Stop the action. 529 */ 530 stop:function () { 531 this._target = null; 532 cc.Action.prototype.stop.call(this); 533 } 534 }); 535 /** creates the action with a set boundary <br/> 536 * creates the action with no boundary set 537 * @param {cc.Node} followedNode 538 * @param {cc.Rect} rect 539 * @return {cc.Follow|Null} returns the cc.Follow object on success 540 * @example 541 * // example 542 * // creates the action with a set boundary 543 * var sprite = cc.Sprite.create("spriteFileName"); 544 * var followAction = cc.Follow.create(sprite, cc.rect(0, 0, s.width * 2 - 100, s.height)); 545 * this.runAction(followAction); 546 * 547 * // creates the action with no boundary set 548 * var sprite = cc.Sprite.create("spriteFileName"); 549 * var followAction = cc.Follow.create(sprite); 550 * this.runAction(followAction); 551 */ 552 cc.Follow.create = function (followedNode, rect) { 553 rect = rect || new cc.RectZero(); 554 var ret = new cc.Follow(); 555 if (rect != null && ret && ret.initWithTarget(followedNode, rect)) 556 return ret; 557 else if (ret && ret.initWithTarget(followedNode)) 558 return ret; 559 return null; 560 }; 561