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 /** 28 * @class 29 * @extends cc.Class 30 */ 31 cc.HashElement = cc.Class.extend(/** @lends cc.HashElement# */{ 32 actions:null, 33 target:null, //ccobject 34 actionIndex:0, 35 currentAction:null, //CCAction 36 currentActionSalvaged:false, 37 paused:false, 38 hh:null, //ut hash handle 39 /** 40 * Constructor 41 */ 42 ctor:function () { 43 this.actions = []; 44 this.target = null; 45 this.actionIndex = 0; 46 this.currentAction = null; //CCAction 47 this.currentActionSalvaged = false; 48 this.paused = false; 49 this.hh = null; //ut hash handle 50 } 51 }); 52 53 /** 54 * cc.ActionManager is a singleton that manages all the actions.<br/> 55 * Normally you won't need to use this singleton directly. 99% of the cases you will use the CCNode interface, 56 * which uses this singleton. 57 * But there are some cases where you might need to use this singleton. <br/> 58 * Examples:<br/> 59 * - When you want to run an action where the target is different from a CCNode.<br/> 60 * - When you want to pause / resume the actions<br/> 61 * @class 62 * @extends cc.Class 63 */ 64 cc.ActionManager = cc.Class.extend({ 65 _hashTargets:null, 66 _arrayTargets:null, 67 _currentTarget:null, 68 _currentTargetSalvaged:false, 69 70 _searchElementByTarget:function (arr, target) { 71 for (var k = 0; k < arr.length; k++) { 72 if (target == arr[k].target) 73 return arr[k]; 74 } 75 return null; 76 }, 77 78 /** 79 * Constructor 80 */ 81 ctor:function () { 82 this._hashTargets = {}; 83 this._arrayTargets = []; 84 this._currentTarget = null; 85 this._currentTargetSalvaged = false; 86 }, 87 88 /** Adds an action with a target. 89 * If the target is already present, then the action will be added to the existing target. 90 * If the target is not present, a new instance of this target will be created either paused or not, and the action will be added to the newly created target. 91 * When the target is paused, the queued actions won't be 'ticked'. 92 * @param {cc.Action} action 93 * @param {cc.Node} target 94 * @param {Boolean} paused 95 */ 96 addAction:function (action, target, paused) { 97 if(!action) 98 throw "cc.ActionManager.addAction(): action must be non-null"; 99 if(!target) 100 throw "cc.ActionManager.addAction(): action must be non-null"; 101 102 //check if the action target already exists 103 var element = this._hashTargets[target.__instanceId]; 104 //if doesnt exists, create a hashelement and push in mpTargets 105 if (!element) { 106 element = new cc.HashElement(); 107 element.paused = paused; 108 element.target = target; 109 this._hashTargets[target.__instanceId] = element; 110 this._arrayTargets.push(element); 111 } 112 //creates a array for that eleemnt to hold the actions 113 this._actionAllocWithHashElement(element); 114 115 element.actions.push(action); 116 action.startWithTarget(target); 117 }, 118 119 /** 120 * Removes all actions from all the targets. 121 */ 122 removeAllActions:function () { 123 var locTargets = this._arrayTargets; 124 for (var i = 0; i < locTargets.length; i++) { 125 var element = locTargets[i]; 126 if (element) 127 this.removeAllActionsFromTarget(element.target, true); 128 } 129 }, 130 /** Removes all actions from a certain target. <br/> 131 * All the actions that belongs to the target will be removed. 132 * @param {object} target 133 * @param {boolean} forceDelete 134 */ 135 removeAllActionsFromTarget:function (target, forceDelete) { 136 // explicit null handling 137 if (target == null) 138 return; 139 var element = this._hashTargets[target.__instanceId]; 140 if (element) { 141 if (element.actions.indexOf(element.currentAction) !== -1 && !(element.currentActionSalvaged)) 142 element.currentActionSalvaged = true; 143 144 element.actions.length = 0; 145 if (this._currentTarget == element && !forceDelete) { 146 this._currentTargetSalvaged = true; 147 } else { 148 this._deleteHashElement(element); 149 } 150 } 151 }, 152 /** Removes an action given an action reference. 153 * @param {cc.Action} action 154 */ 155 removeAction:function (action) { 156 // explicit null handling 157 if (action == null) 158 return; 159 var target = action.getOriginalTarget(); 160 var element = this._hashTargets[target.__instanceId]; 161 162 if (element) { 163 for (var i = 0; i < element.actions.length; i++) { 164 if (element.actions[i] == action) { 165 element.actions.splice(i, 1); 166 break; 167 } 168 } 169 } else { 170 cc.log("cocos2d: removeAction: Target not found"); 171 } 172 }, 173 174 /** Removes an action given its tag and the target 175 * @param {Number} tag 176 * @param {object} target 177 */ 178 removeActionByTag:function (tag, target) { 179 if(tag == cc.ACTION_TAG_INVALID) 180 cc.log("cc.ActionManager.removeActionByTag(): an invalid tag"); 181 if(!target) 182 throw "cc.ActionManager.removeActionByTag(): target must be non-null"; 183 184 var element = this._hashTargets[target.__instanceId]; 185 186 if (element) { 187 var limit = element.actions.length; 188 for (var i = 0; i < limit; ++i) { 189 var action = element.actions[i]; 190 if (action && action.getTag() === tag && action.getOriginalTarget() == target) { 191 this._removeActionAtIndex(i, element); 192 break; 193 } 194 } 195 } 196 }, 197 198 /** Gets an action given its tag an a target 199 * @param {Number} tag 200 * @param {object} target 201 * @return {cc.Action|Null} return the Action with the given tag on success 202 */ 203 getActionByTag:function (tag, target) { 204 if(tag == cc.ACTION_TAG_INVALID) 205 cc.log("cc.ActionManager.getActionByTag(): an invalid tag"); 206 207 var element = this._hashTargets[target.__instanceId]; 208 if (element) { 209 if (element.actions != null) { 210 for (var i = 0; i < element.actions.length; ++i) { 211 var action = element.actions[i]; 212 if (action && action.getTag() === tag) 213 return action; 214 } 215 } 216 cc.log("cocos2d : getActionByTag(tag =" + tag + "): Action not found"); 217 } 218 return null; 219 }, 220 221 222 /** Returns the numbers of actions that are running in a certain target. <br/> 223 * Composable actions are counted as 1 action. <br/> 224 * Example: <br/> 225 * - If you are running 1 Sequence of 7 actions, it will return 1. <br/> 226 * - If you are running 7 Sequences of 2 actions, it will return 7. 227 * @param {object} target 228 * @return {Number} 229 */ 230 numberOfRunningActionsInTarget:function (target) { 231 var element = this._hashTargets[target.__instanceId]; 232 if (element) 233 return (element.actions) ? element.actions.length : 0; 234 235 return 0; 236 }, 237 /** Pauses the target: all running actions and newly added actions will be paused. 238 * @param {object} target 239 */ 240 pauseTarget:function (target) { 241 var element = this._hashTargets[target.__instanceId]; 242 if (element) 243 element.paused = true; 244 }, 245 /** Resumes the target. All queued actions will be resumed. 246 * @param {object} target 247 */ 248 resumeTarget:function (target) { 249 var element = this._hashTargets[target.__instanceId]; 250 if (element) 251 element.paused = false; 252 }, 253 254 /** 255 * Pauses all running actions, returning a list of targets whose actions were paused. 256 * @return {Array} a list of targets whose actions were paused. 257 */ 258 pauseAllRunningActions:function(){ 259 var idsWithActions = []; 260 var locTargets = this._arrayTargets; 261 for(var i = 0; i< locTargets.length; i++){ 262 var element = locTargets[i]; 263 if(element && !element.paused){ 264 element.paused = true; 265 idsWithActions.push(element.target); 266 } 267 } 268 return idsWithActions; 269 }, 270 271 /** 272 * Resume a set of targets (convenience function to reverse a pauseAllRunningActions call) 273 * @param {Array} targetsToResume 274 */ 275 resumeTargets:function(targetsToResume){ 276 if(!targetsToResume) 277 return; 278 279 for(var i = 0 ; i< targetsToResume.length; i++){ 280 if(targetsToResume[i]) 281 this.resumeTarget(targetsToResume[i]); 282 } 283 }, 284 285 /** purges the shared action manager. It releases the retained instance. <br/> 286 * because it uses this, so it can not be static 287 */ 288 purgeSharedManager:function () { 289 cc.director.getScheduler().unscheduleUpdateForTarget(this); 290 }, 291 292 //protected 293 _removeActionAtIndex:function (index, element) { 294 var action = element.actions[index]; 295 296 if ((action == element.currentAction) && (!element.currentActionSalvaged)) 297 element.currentActionSalvaged = true; 298 299 element.actions.splice(index, 1); 300 301 // update actionIndex in case we are in tick. looping over the actions 302 if (element.actionIndex >= index) 303 element.actionIndex--; 304 305 if (element.actions.length == 0) { 306 if (this._currentTarget == element) { 307 this._currentTargetSalvaged = true; 308 } else { 309 this._deleteHashElement(element); 310 } 311 } 312 }, 313 314 _deleteHashElement:function (element) { 315 if (element) { 316 delete this._hashTargets[element.target.__instanceId]; 317 cc.arrayRemoveObject(this._arrayTargets, element); 318 element.actions = null; 319 element.target = null; 320 } 321 }, 322 323 _actionAllocWithHashElement:function (element) { 324 // 4 actions per Node by default 325 if (element.actions == null) { 326 element.actions = []; 327 } 328 }, 329 330 /** 331 * @param {Number} dt delta time in seconds 332 */ 333 update:function (dt) { 334 var locTargets = this._arrayTargets , locCurrTarget; 335 for (var elt = 0; elt < locTargets.length; elt++) { 336 this._currentTarget = locTargets[elt]; 337 locCurrTarget = this._currentTarget; 338 //this._currentTargetSalvaged = false; 339 if (!locCurrTarget.paused) { 340 // The 'actions' CCMutableArray may change while inside this loop. 341 for (locCurrTarget.actionIndex = 0; locCurrTarget.actionIndex < locCurrTarget.actions.length; 342 locCurrTarget.actionIndex++) { 343 locCurrTarget.currentAction = locCurrTarget.actions[locCurrTarget.actionIndex]; 344 if (!locCurrTarget.currentAction) 345 continue; 346 347 locCurrTarget.currentActionSalvaged = false; 348 locCurrTarget.currentAction.step(dt); 349 if (locCurrTarget.currentActionSalvaged) { 350 // The currentAction told the node to remove it. To prevent the action from 351 // accidentally deallocating itself before finishing its step, we retained 352 // it. Now that step is done, it's safe to release it. 353 locCurrTarget.currentAction = null;//release 354 } else if (locCurrTarget.currentAction.isDone()) { 355 locCurrTarget.currentAction.stop(); 356 var action = locCurrTarget.currentAction; 357 // Make currentAction nil to prevent removeAction from salvaging it. 358 locCurrTarget.currentAction = null; 359 this.removeAction(action); 360 } 361 362 locCurrTarget.currentAction = null; 363 } 364 } 365 366 // elt, at this moment, is still valid 367 // so it is safe to ask this here (issue #490) 368 369 // only delete currentTarget if no actions were scheduled during the cycle (issue #481) 370 if (this._currentTargetSalvaged && locCurrTarget.actions.length === 0) { 371 this._deleteHashElement(locCurrTarget); 372 } 373 } 374 } 375 }); 376