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 cc.Assert(action != null, ""); 291 this._innerAction = action; 292 this._speed = speed; 293 return true; 294 }, 295 296 /** 297 * returns a clone of action 298 * @returns {cc.Speed} 299 */ 300 clone:function () { 301 var action = new cc.Speed(); 302 action.initWithAction(this._innerAction.clone(), this._speed); 303 return action; 304 }, 305 306 /** 307 * @param {cc.Node} target 308 */ 309 startWithTarget:function (target) { 310 cc.Action.prototype.startWithTarget.call(this, target); 311 this._innerAction.startWithTarget(target); 312 }, 313 314 /** 315 * Stop the action 316 */ 317 stop:function () { 318 this._innerAction.stop(); 319 cc.Action.prototype.stop.call(this); 320 }, 321 322 /** 323 * @param {Number} dt 324 */ 325 step:function (dt) { 326 this._innerAction.step(dt * this._speed); 327 }, 328 329 /** 330 * @return {Boolean} 331 */ 332 isDone:function () { 333 return this._innerAction.isDone(); 334 }, 335 336 /** 337 * @return {cc.ActionInterval} 338 */ 339 reverse:function () { 340 return (cc.Speed.create(this._innerAction.reverse(), this._speed)); 341 }, 342 343 /** 344 * 345 * @param {cc.ActionInterval} action 346 */ 347 setInnerAction:function (action) { 348 if (this._innerAction != action) { 349 this._innerAction = action; 350 } 351 }, 352 353 /** 354 * 355 * @return {cc.ActionInterval} 356 */ 357 getInnerAction:function () { 358 return this._innerAction; 359 } 360 }); 361 /** creates the action 362 * 363 * @param {cc.ActionInterval} action 364 * @param {Number} speed 365 * @return {cc.Speed} 366 */ 367 cc.Speed.create = function (action, speed) { 368 var ret = new cc.Speed(); 369 if (ret && ret.initWithAction(action, speed)) 370 return ret; 371 return null; 372 }; 373 374 /** 375 * cc.Follow is an action that "follows" a node. 376 377 * @example 378 * //example 379 * //Instead of using cc.Camera as a "follower", use this action instead. 380 * layer.runAction(cc.Follow.actionWithTarget(hero)); 381 382 * @class 383 * @extends cc.Action 384 */ 385 cc.Follow = cc.Action.extend(/** @lends cc.Follow# */{ 386 // node to follow 387 _followedNode:null, 388 // whether camera should be limited to certain area 389 _boundarySet:false, 390 // if screen size is bigger than the boundary - update not needed 391 _boundaryFullyCovered:false, 392 // fast access to the screen dimensions 393 _halfScreenSize:null, 394 _fullScreenSize:null, 395 396 /** world leftBoundary 397 * @Type {Number} 398 */ 399 leftBoundary:0.0, 400 /** world rightBoundary 401 * @Type Number 402 */ 403 rightBoundary:0.0, 404 /** world topBoundary 405 * @Type Number 406 */ 407 topBoundary:0.0, 408 /** world bottomBoundary 409 * @Type {Number} 410 */ 411 bottomBoundary:0.0, 412 _worldRect:null, 413 414 ctor:function () { 415 cc.Action.prototype.ctor.call(this); 416 this._followedNode = null; 417 this._boundarySet = false; 418 419 this._boundaryFullyCovered = false; 420 this._halfScreenSize = null; 421 this._fullScreenSize = null; 422 423 this.leftBoundary = 0.0; 424 this.rightBoundary = 0.0; 425 this.topBoundary = 0.0; 426 this.bottomBoundary = 0.0; 427 this._worldRect = cc.RectZero(); 428 }, 429 430 clone:function () { 431 var action = new cc.Follow(); 432 var locRect = this._worldRect; 433 var rect = new cc.Rect(locRect.x, locRect.y, locRect.width, locRect.height); 434 action.initWithTarget(this._followedNode, rect); 435 return action; 436 }, 437 438 /** 439 * @return {Boolean} 440 */ 441 isBoundarySet:function () { 442 return this._boundarySet; 443 }, 444 445 /** alter behavior - turn on/off boundary 446 * @param {Boolean} value 447 */ 448 setBoudarySet:function (value) { 449 this._boundarySet = value; 450 }, 451 452 /** initializes the action 453 * initializes the action with a set boundary 454 * @param {cc.Node} followedNode 455 * @param {cc.Rect} [rect=] 456 * @return {Boolean} 457 */ 458 initWithTarget:function (followedNode, rect) { 459 cc.Assert(followedNode != null, ""); 460 461 rect = rect || cc.RectZero(); 462 this._followedNode = followedNode; 463 this._worldRect = rect; 464 465 this._boundarySet = !cc._rectEqualToZero(rect); 466 467 this._boundaryFullyCovered = false; 468 469 var winSize = cc.Director.getInstance().getWinSize(); 470 this._fullScreenSize = cc.p(winSize.width, winSize.height); 471 this._halfScreenSize = cc.pMult(this._fullScreenSize, 0.5); 472 473 if (this._boundarySet) { 474 this.leftBoundary = -((rect.x + rect.width) - this._fullScreenSize.x); 475 this.rightBoundary = -rect.x; 476 this.topBoundary = -rect.y; 477 this.bottomBoundary = -((rect.y + rect.height) - this._fullScreenSize.y); 478 479 if (this.rightBoundary < this.leftBoundary) { 480 // screen width is larger than world's boundary width 481 //set both in the middle of the world 482 this.rightBoundary = this.leftBoundary = (this.leftBoundary + this.rightBoundary) / 2; 483 } 484 if (this.topBoundary < this.bottomBoundary) { 485 // screen width is larger than world's boundary width 486 //set both in the middle of the world 487 this.topBoundary = this.bottomBoundary = (this.topBoundary + this.bottomBoundary) / 2; 488 } 489 490 if ((this.topBoundary == this.bottomBoundary) && (this.leftBoundary == this.rightBoundary)) 491 this._boundaryFullyCovered = true; 492 } 493 return true; 494 }, 495 496 /** 497 * @param {Number} dt 498 */ 499 step:function (dt) { 500 var tempPosX = this._followedNode.getPositionX(); 501 var tempPosY = this._followedNode.getPositionY(); 502 tempPosX = this._halfScreenSize.x - tempPosX; 503 tempPosY = this._halfScreenSize.y - tempPosY; 504 505 if (this._boundarySet) { 506 // whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased 507 if (this._boundaryFullyCovered) 508 return; 509 510 this._target.setPosition(cc.clampf(tempPosX, this.leftBoundary, this.rightBoundary), 511 cc.clampf(tempPosY, this.bottomBoundary, this.topBoundary)); 512 } else { 513 this._target.setPosition(tempPosX, tempPosY); 514 } 515 }, 516 517 /** 518 * @return {Boolean} 519 */ 520 isDone:function () { 521 return ( !this._followedNode.isRunning() ); 522 }, 523 524 /** 525 * Stop the action. 526 */ 527 stop:function () { 528 this._target = null; 529 cc.Action.prototype.stop.call(this); 530 } 531 }); 532 /** creates the action with a set boundary <br/> 533 * creates the action with no boundary set 534 * @param {cc.Node} followedNode 535 * @param {cc.Rect} rect 536 * @return {cc.Follow|Null} returns the cc.Follow object on success 537 * @example 538 * // example 539 * // creates the action with a set boundary 540 * var sprite = cc.Sprite.create("spriteFileName"); 541 * var followAction = cc.Follow.create(sprite, cc.rect(0, 0, s.width * 2 - 100, s.height)); 542 * this.runAction(followAction); 543 * 544 * // creates the action with no boundary set 545 * var sprite = cc.Sprite.create("spriteFileName"); 546 * var followAction = cc.Follow.create(sprite); 547 * this.runAction(followAction); 548 */ 549 cc.Follow.create = function (followedNode, rect) { 550 rect = rect || new cc.RectZero(); 551 var ret = new cc.Follow(); 552 if (rect != null && ret && ret.initWithTarget(followedNode, rect)) 553 return ret; 554 else if (ret && ret.initWithTarget(followedNode)) 555 return ret; 556 return null; 557 }; 558