1 /****************************************************************************
  2  Copyright (c) 2011-2012 cocos2d-x.org
  3  Copyright (c) 2013-2014 Chukong Technologies Inc.
  4 
  5  http://www.cocos2d-x.org
  6 
  7  Permission is hereby granted, free of charge, to any person obtaining a copy
  8  of this software and associated documentation files (the "Software"), to deal
  9  in the Software without restriction, including without limitation the rights
 10  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 11  copies of the Software, and to permit persons to whom the Software is
 12  furnished to do so, subject to the following conditions:
 13 
 14  The above copyright notice and this permission notice shall be included in
 15  all copies or substantial portions of the Software.
 16 
 17  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 18  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 19  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 20  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 21  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 22  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 23  THE SOFTWARE.
 24  ****************************************************************************/
 25 
 26 /**
 27  * The display manager for CocoStudio Armature bone.
 28  * @Class ccs.DisplayManager
 29  * @extend cc.Class
 30  */
 31 ccs.DisplayManager = ccs.Class.extend(/** @lends ccs.DisplayManager */{
 32     _decoDisplayList:null,
 33     _currentDecoDisplay:null,
 34     _displayRenderNode:null,
 35     _displayIndex: null,
 36     _forceChangeDisplay:false,
 37     _bone:null,
 38     _visible:true,
 39     _displayType: null,
 40 
 41     /**
 42      * Construction of ccs.DisplayManager.
 43      */
 44     ctor:function () {
 45         this._decoDisplayList = [];
 46         this._currentDecoDisplay = null;
 47         this._displayRenderNode = null;
 48         this._displayIndex = null;
 49         this._forceChangeDisplay = false;
 50         this._bone = null;
 51         this._visible = true;
 52         this._displayType = ccs.DISPLAY_TYPE_MAX;
 53     },
 54 
 55     /**
 56      * Initializes a ccs.DisplayManager.
 57      * @param bone
 58      * @returns {boolean}
 59      */
 60     init:function (bone) {
 61         this._bone = bone;
 62         this.initDisplayList(bone.getBoneData());
 63         return true;
 64     },
 65 
 66     /**
 67      * <p>
 68      *     Add display and use  _DisplayData init the display.                              <br/>
 69      *     If index already have a display, then replace it.                                <br/>
 70      *     If index is current display index, then also change display to _index            <br/>
 71      * </p>
 72      * @param {ccs.DisplayData|cc.Node} display it include the display information, like DisplayType. If you want to create a sprite display, then create a SpriteDisplayData param
 73      * @param {Number} index  the index of the display you want to replace or add to. -1 : append display from back
 74      */
 75     addDisplay: function (display, index) {
 76         var decoDisplay, locDisplayList = this._decoDisplayList;
 77         if( (index >= 0) && (index < locDisplayList.length) )
 78             decoDisplay = locDisplayList[index];
 79         else{
 80             decoDisplay = ccs.DecorativeDisplay.create();
 81             locDisplayList.push(decoDisplay);
 82         }
 83 
 84         if(display instanceof ccs.DisplayData){
 85             cc.displayFactory.addDisplay(this._bone, decoDisplay, display);
 86             //! if changed display index is current display index, then change current display to the new display
 87             if(index == this._displayIndex) {
 88                 this._displayIndex = -1;
 89                 this.changeDisplayWithIndex(index, false);
 90             }
 91             return;
 92         }
 93 
 94         var displayData = null;
 95         if (display instanceof ccs.Skin) {
 96             display.setBone(this._bone);
 97             displayData = new ccs.SpriteDisplayData();
 98             ccs.displayFactory.initSpriteDisplay(this._bone, decoDisplay, display.getDisplayName(), display);
 99 
100             var spriteDisplayData = decoDisplay.getDisplayData();
101             if (spriteDisplayData instanceof ccs.SpriteDisplayData) {
102                 display.setSkinData(spriteDisplayData.skinData);
103                 displayData.skinData = spriteDisplayData.skinData;
104             } else {
105                 var find = false;
106                 for (var i = locDisplayList.length - 2; i >= 0; i--) {
107                     var dd = locDisplayList[i];
108                     var sdd = dd.getDisplayData();
109                     if (sdd instanceof ccs.SpriteDisplayData) {
110                         find = true;
111                         display.setSkinData(sdd.skinData);
112                         displayData.skinData = sdd.skinData;
113                         break;
114                     }
115                 }
116                 if (!find)
117                     display.setSkinData(new ccs.BaseData());
118             }
119         } else if (display instanceof cc.ParticleSystem){
120             displayData = new ccs.ParticleDisplayData();
121             display.removeFromParent();
122             display.cleanup();
123             var armature = this._bone.getArmature();
124             if (armature)
125                 display.setParent(armature);
126         } else if(display instanceof ccs.Armature) {
127             displayData = new ccs.ArmatureDisplayData();
128             displayData.displayName = display.getName();
129             display.setParentBone(this._bone);
130         } else
131             displayData = new ccs.DisplayData();
132         decoDisplay.setDisplay(display);
133         decoDisplay.setDisplayData(displayData);
134 
135         //! if changed display index is current display index, then change current display to the new display
136         if(index == this._displayIndex) {
137             this._displayIndex = -1;
138             this.changeDisplayWithIndex(index, false);
139         }
140     },
141 
142     _addDisplayOther:function(decoDisplay,display){
143         var displayData = null;
144         if (display instanceof ccs.Skin){
145             var skin = display;
146             skin.setBone(this._bone);
147             displayData = new ccs.SpriteDisplayData();
148             displayData.displayName = skin.getDisplayName();
149             ccs.displayFactory.initSpriteDisplay(this._bone, decoDisplay, skin.getDisplayName(), skin);
150             var spriteDisplayData = decoDisplay.getDisplayData();
151             if (spriteDisplayData instanceof ccs.SpriteDisplayData)
152                 skin.setSkinData(spriteDisplayData.skinData);
153             else{
154                 var find = false;
155                 for (var i = this._decoDisplayList.length - 2; i >= 0; i--) {
156                     var dd = this._decoDisplayList[i];
157                     var sdd = dd.getDisplayData();
158                     if (sdd) {
159                         find = true;
160                         skin.setSkinData(sdd.skinData);
161                         displayData.skinData = sdd.skinData;
162                         break;
163                     }
164                 }
165                 if (!find) {
166                     skin.setSkinData(new ccs.BaseData());
167                 }
168                 skin.setSkinData(new ccs.BaseData());
169             }
170                 
171         }
172         else if (display instanceof cc.ParticleSystem){
173             displayData = new ccs.ParticleDisplayData();
174             displayData.displayName = display._plistFile;
175         }
176         else if (display instanceof ccs.Armature){
177             displayData = new ccs.ArmatureDisplayData();
178             displayData.displayName = display.getName();
179             display.setParentBone(this._bone);
180         }
181         else  {
182             displayData = new ccs.DisplayData();
183         }
184         decoDisplay.setDisplay(display);
185         decoDisplay.setDisplayData(displayData);
186     },
187 
188     /**
189      * Removes display node from list.
190      * @param {Number} index
191      */
192     removeDisplay:function (index) {
193         this._decoDisplayList.splice(index, 1);
194         if (index === this._displayIndex) {
195             this.setCurrentDecorativeDisplay(null);
196             this._displayIndex = -1;
197         }
198     },
199 
200     /**
201      * Returns the display node list.
202      * @returns {Array}
203      */
204     getDecorativeDisplayList:function(){
205         return this._decoDisplayList;
206     },
207 
208     /**
209      * <p>
210      *     Change display by index. You can just use this method to change display in the display list.            <br/>
211      *     The display list is just used for this bone, and it is the displays you may use in every frame.         <br/>
212      *     Note : if index is the same with prev index, the method will not effect                                 <br/>
213      * </p>
214      * @param {Number} index  The index of the display you want to change
215      * @param {Boolean} force  If true, then force change display to specified display, or current display will set to  display index edit in the flash every key frame.
216      */
217     changeDisplayWithIndex:function (index, force) {
218         if (index >= this._decoDisplayList.length) {
219             cc.log("the index value is out of range");
220             return;
221         }
222         this._forceChangeDisplay = force;
223 
224         //if index is equal to current display index,then do nothing
225         if (this._displayIndex == index)
226             return;
227 
228         this._displayIndex = index;
229 
230         //! If displayIndex < 0, it means you want to hide you display
231         if (index < 0) {
232             if(this._displayRenderNode) {
233                 this._displayRenderNode.removeFromParent(true);
234                 this.setCurrentDecorativeDisplay(null);
235             }
236             return;
237         }
238         this.setCurrentDecorativeDisplay(this._decoDisplayList[index]);
239     },
240 
241     /**
242      * Change display by name. @see changeDisplayWithIndex.
243      * @param {String} name
244      * @param {Boolean} force
245      */
246     changeDisplayWithName: function (name, force) {
247         var locDisplayList = this._decoDisplayList;
248         for (var i = 0; i < locDisplayList.length; i++) {
249             if (locDisplayList[i].getDisplayData().displayName == name) {
250                 this.changeDisplayWithIndex(i, force);
251                 break;
252             }
253         }
254     },
255 
256     /**
257      * Sets current decorative display.
258      * @param {ccs.DecorativeDisplay} decoDisplay
259      */
260     setCurrentDecorativeDisplay:function (decoDisplay) {
261         var locCurrentDecoDisplay = this._currentDecoDisplay;
262         if (ccs.ENABLE_PHYSICS_CHIPMUNK_DETECT || ccs.ENABLE_PHYSICS_SAVE_CALCULATED_VERTEX) {
263             if (locCurrentDecoDisplay && locCurrentDecoDisplay.getColliderDetector())
264                 locCurrentDecoDisplay.getColliderDetector().setActive(false);
265         }
266 
267         this._currentDecoDisplay = decoDisplay;
268         locCurrentDecoDisplay = this._currentDecoDisplay;
269         if (ccs.ENABLE_PHYSICS_CHIPMUNK_DETECT || ccs.ENABLE_PHYSICS_SAVE_CALCULATED_VERTEX) {
270             if (locCurrentDecoDisplay && locCurrentDecoDisplay.getColliderDetector())
271                 locCurrentDecoDisplay.getColliderDetector().setActive(true);
272         }
273 
274         var displayRenderNode = (!locCurrentDecoDisplay) ? null : locCurrentDecoDisplay.getDisplay();
275 
276         var locRenderNode = this._displayRenderNode, locBone = this._bone;
277         if (locRenderNode) {
278             if (locRenderNode instanceof ccs.Armature)
279                 locBone.setChildArmature(null);
280             locRenderNode.removeFromParent(true);
281         }
282         this._displayRenderNode = displayRenderNode;
283 
284         if (displayRenderNode) {
285             if (displayRenderNode instanceof ccs.Armature) {
286                 this._bone.setChildArmature(displayRenderNode);
287                 displayRenderNode.setParentBone(this._bone);
288             } else if (displayRenderNode instanceof cc.ParticleSystem) {
289                 if (displayRenderNode instanceof ccs.Armature) {
290                     locBone.setChildArmature(displayRenderNode);
291                     displayRenderNode.setParentBone(locBone);
292                 } else if (displayRenderNode instanceof cc.ParticleSystem)
293                     displayRenderNode.resetSystem();
294             }
295 
296             displayRenderNode.setColor(locBone.getDisplayedColor());
297             displayRenderNode.setOpacity(locBone.getDisplayedOpacity());
298 
299             this._displayRenderNode.setVisible(this._visible);
300             this._displayType = this._currentDecoDisplay.getDisplayData().displayType;
301         }else
302             this._displayType = ccs.DISPLAY_TYPE_MAX;
303     },
304 
305     /**
306      *  Returns the current display render node.
307      * @returns {cc.Node}
308      */
309     getDisplayRenderNode:function () {
310         return this._displayRenderNode;
311     },
312 
313     /**
314      * Returns the type of display render node.
315      * @returns {Number}
316      */
317     getDisplayRenderNodeType:function(){
318         return this._displayType;
319     },
320 
321     /**
322      * Returns the index of display render node.
323      * @returns {Number}
324      */
325     getCurrentDisplayIndex:function () {
326         return this._displayIndex;
327     },
328 
329     /**
330      * Returns the current decorative display
331      * @returns {ccs.DecorativeDisplay}
332      */
333     getCurrentDecorativeDisplay:function () {
334         return this._currentDecoDisplay;
335     },
336 
337     /**
338      * Gets a decorative display by index.
339      * @param index
340      * @returns {ccs.DecorativeDisplay}
341      */
342     getDecorativeDisplayByIndex:function (index) {
343         return this._decoDisplayList[index];
344     },
345 
346     /**
347      * <p>
348      *  Use BoneData to init the display list.
349      *  If display is a sprite, and it have texture info in the TextureData, then use TextureData to init the display node's anchor point
350      *  If the display is a Armature, then create a new Armature
351      * </p>
352      * @param {ccs.BoneData} boneData
353      */
354     initDisplayList:function (boneData) {
355         this._decoDisplayList.length = 0;
356         if (!boneData)
357             return;
358         var displayList = boneData.displayDataList, decoList = this._decoDisplayList, locBone = this._bone;
359         for (var i = 0; i < displayList.length; i++) {
360             var displayData = displayList[i];
361             var decoDisplay = ccs.DecorativeDisplay.create();
362             decoDisplay.setDisplayData(displayData);
363             ccs.displayFactory.createDisplay(locBone, decoDisplay);
364             decoList.push(decoDisplay);
365         }
366     },
367 
368     /**
369      * Check if the position is inside the bone.
370      * @param {cc.Point|Number} point
371      * @param {Number} [y]
372      * @returns {boolean}
373      */
374     containPoint: function (point, y) {
375         if (!this._visible || this._displayIndex < 0)
376             return false;
377 
378         if (y !== undefined)
379             point = cc.p(point, y);
380 
381         if(this._currentDecoDisplay.getDisplayData().displayType == ccs.DISPLAY_TYPE_SPRITE){
382             /*
383              *  First we first check if the point is in the sprite content rect. If false, then we continue to check
384              *  the contour point. If this step is also false, then we can say the bone not contain this point.
385              *
386              */
387             var sprite = this._currentDecoDisplay.getDisplay();
388             sprite = sprite.getChildByTag(0);
389             return ccs.SPRITE_CONTAIN_POINT_WITH_RETURN(sprite, point);
390         }
391         return false;
392     },
393 
394     /**
395      * <p>
396      *  Sets whether the display is visible                                               <br/>
397      *  The default value is true, a node is default to visible
398      * </p>
399      * @param {boolean} visible
400      */
401     setVisible:function (visible) {
402         if (!this._displayRenderNode)
403             return;
404         this._visible = visible;
405         this._displayRenderNode.setVisible(visible);
406     },
407 
408     /**
409      * Determines if the display is visible
410      * @returns {boolean} true if the node is visible, false if the node is hidden.
411      */
412     isVisible:function () {
413         return this._visible;
414     },
415 
416     getContentSize:function () {
417         if (!this._displayRenderNode)
418             return cc.size(0, 0);
419         return this._displayRenderNode.getContentSize();
420     },
421 
422     getBoundingBox:function () {
423         if (!this._displayRenderNode)
424             return cc.rect(0, 0, 0, 0);
425         return this._displayRenderNode.getBoundingBox();
426     },
427 
428     getAnchorPoint:function () {
429         if (!this._displayRenderNode)
430             return  cc.p(0, 0);
431         return this._displayRenderNode.getAnchorPoint();
432     },
433 
434     getAnchorPointInPoints:function () {
435         if (!this._displayRenderNode)
436             return  cc.p(0, 0);
437         return this._displayRenderNode.getAnchorPointInPoints();
438     },
439 
440     getForceChangeDisplay:function () {
441         return this._forceChangeDisplay;
442     },
443 
444     release:function () {
445         this._decoDisplayList = null;
446         if (this._displayRenderNode) {
447             this._displayRenderNode.removeFromParent(true);
448             this._displayRenderNode = null;
449         }
450     }
451 });
452 
453 /**
454  * Allocates and initializes a display manager with ccs.Bone.
455  * @param {ccs.Bone} bone
456  * @returns {ccs.DisplayManager}
457  */
458 ccs.DisplayManager.create = function (bone) {
459     var displayManager = new ccs.DisplayManager();
460     if (displayManager && displayManager.init(bone))
461         return displayManager;
462     return null;
463 };