/* * cocos2d for iPhone: http://www.cocos2d-iphone.org * * Copyright (c) 2009 Valentin Milea * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #import "CCNode.h" #import "CCGrid.h" #import "CCDirector.h" #import "CCActionManager.h" #import "CCCamera.h" #import "CCScheduler.h" #import "ccConfig.h" #import "ccMacros.h" #import "Support/CGPointExtension.h" #import "Support/ccCArray.h" #import "Support/TransformUtils.h" #import "ccMacros.h" #import #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED #import "Platforms/iOS/CCDirectorIOS.h" #endif #if CC_COCOSNODE_RENDER_SUBPIXEL #define RENDER_IN_SUBPIXEL #else #define RENDER_IN_SUBPIXEL (NSInteger) #endif @interface CCNode () // lazy allocs -(void) childrenAlloc; // helper that reorder a child -(void) insertChild:(CCNode*)child z:(NSInteger)z; // used internally to alter the zOrder variable. DON'T call this method manually -(void) _setZOrder:(NSInteger) z; -(void) detachChild:(CCNode *)child cleanup:(BOOL)doCleanup; @end @implementation CCNode @synthesize children = children_; @synthesize visible = visible_; @synthesize parent = parent_; @synthesize grid = grid_; @synthesize zOrder = zOrder_; @synthesize tag = tag_; @synthesize vertexZ = vertexZ_; @synthesize isRunning = isRunning_; @synthesize userData = userData_; #pragma mark CCNode - Transform related properties @synthesize rotation = rotation_, scaleX = scaleX_, scaleY = scaleY_; @synthesize skewX = skewX_, skewY = skewY_; @synthesize position = position_, positionInPixels = positionInPixels_; @synthesize anchorPoint = anchorPoint_, anchorPointInPixels = anchorPointInPixels_; @synthesize contentSize = contentSize_, contentSizeInPixels = contentSizeInPixels_; @synthesize isRelativeAnchorPoint = isRelativeAnchorPoint_; // getters synthesized, setters explicit -(void) setSkewX:(float)newSkewX { skewX_ = newSkewX; isTransformDirty_ = isInverseDirty_ = YES; #if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX isTransformGLDirty_ = YES; #endif } -(void) setSkewY:(float)newSkewY { skewY_ = newSkewY; isTransformDirty_ = isInverseDirty_ = YES; #if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX isTransformGLDirty_ = YES; #endif } -(void) setRotation: (float)newRotation { rotation_ = newRotation; isTransformDirty_ = isInverseDirty_ = YES; #if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX isTransformGLDirty_ = YES; #endif } -(void) setScaleX: (float)newScaleX { scaleX_ = newScaleX; isTransformDirty_ = isInverseDirty_ = YES; #if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX isTransformGLDirty_ = YES; #endif } -(void) setScaleY: (float)newScaleY { scaleY_ = newScaleY; isTransformDirty_ = isInverseDirty_ = YES; #if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX isTransformGLDirty_ = YES; #endif } -(void) setPosition: (CGPoint)newPosition { position_ = newPosition; if( CC_CONTENT_SCALE_FACTOR() == 1 ) positionInPixels_ = position_; else positionInPixels_ = ccpMult( newPosition, CC_CONTENT_SCALE_FACTOR() ); isTransformDirty_ = isInverseDirty_ = YES; #if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX isTransformGLDirty_ = YES; #endif } -(void) setPositionInPixels:(CGPoint)newPosition { positionInPixels_ = newPosition; if( CC_CONTENT_SCALE_FACTOR() == 1 ) position_ = positionInPixels_; else position_ = ccpMult( newPosition, 1/CC_CONTENT_SCALE_FACTOR() ); isTransformDirty_ = isInverseDirty_ = YES; #if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX isTransformGLDirty_ = YES; #endif } -(void) setIsRe
name: "Hidden Room"
display_name: "Revitalized"
panels {
  name: "HIDDEN"
  path: "Panels/panel_3"
  clue: "hidden"
  answer: "hi"
  symbols: SPARKLES
}
: (CCNode*)child cleanup:(BOOL)cleanup { // explicit nil handling if (child == nil) return; if ( [children_ containsObject:child] ) [self detachChild:child cleanup:cleanup]; } -(void) removeChildByTag:(NSInteger)aTag cleanup:(BOOL)cleanup { NSAssert( aTag != kCCNodeTagInvalid, @"Invalid tag"); CCNode *child = [self getChildByTag:aTag]; if (child == nil) CCLOG(@"cocos2d: removeChildByTag: child not found!"); else [self removeChild:child cleanup:cleanup]; } -(void) removeAllChildrenWithCleanup:(BOOL)cleanup { // not using detachChild improves speed here CCNode *c; CCARRAY_FOREACH(children_, c) { // IMPORTANT: // -1st do onExit // -2nd cleanup if (isRunning_) [c onExit]; if (cleanup) [c cleanup]; // set parent nil at the end (issue #476) [c setParent:nil]; } [children_ removeAllObjects]; } -(void) detachChild:(CCNode *)child cleanup:(BOOL)doCleanup { // IMPORTANT: // -1st do onExit // -2nd cleanup if (isRunning_) [child onExit]; // If you don't do cleanup, the child's actions will not get removed and the // its scheduledSelectors_ dict will not get released! if (doCleanup) [child cleanup]; // set parent nil at the end (issue #476) [child setParent:nil]; [children_ removeObject:child]; } // used internally to alter the zOrder variable. DON'T call this method manually -(void) _setZOrder:(NSInteger) z { zOrder_ = z; } // helper used by reorderChild & add -(void) insertChild:(CCNode*)child z:(NSInteger)z { NSUInteger index=0; CCNode *a = [children_ lastObject]; // quick comparison to improve performance if (!a || a.zOrder <= z) [children_ addObject:child]; else { CCARRAY_FOREACH(children_, a) { if ( a.zOrder > z ) { [children_ insertObject:child atIndex:index]; break; } index++; } } [child _setZOrder:z]; } -(void) reorderChild:(CCNode*) child z:(NSInteger)z { NSAssert( child != nil, @"Child must be non-nil"); [child retain]; [children_ removeObject:child]; [self insertChild:child z:z]; [child release]; } #pragma mark CCNode Draw -(void) draw { // override me // Only use this function to draw your staff. // DON'T draw your stuff outside this method } -(void) visit { // quick return if not visible if (!visible_) return; glPushMatrix(); if ( grid_ && grid_.active) { [grid_ beforeDraw]; [self transformAncestors]; } [self transform]; if(children_) { ccArray *arrayData = children_->data; NSUInteger i = 0; // draw children zOrder < 0 for( ; i < arrayData->num; i++ ) { CCNode *child = arrayData->arr[i]; if ( [child zOrder] < 0 ) [child visit]; else break; } // self draw [self draw]; // draw children zOrder >= 0 for( ; i < arrayData->num; i++ ) { CCNode *child = arrayData->arr[i]; [child visit]; } } else [self draw]; if ( grid_ && grid_.active) [grid_ afterDraw:self]; glPopMatrix(); } #pragma mark CCNode - Transformations -(void) transformAncestors { if( parent_ ) { [parent_ transformAncestors]; [parent_ transform]; } } -(void) transform { // transformations #if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX // BEGIN alternative -- using cached transform // if( isTransformGLDirty_ ) { CGAffineTransform t = [self nodeToParentTransform]; CGAffineToGL(&t, transformGL_); isTransformGLDirty_ = NO; } glMultMatrixf(transformGL_); if( vertexZ_ ) glTranslatef(0, 0, vertexZ_); // XXX: Expensive calls. Camera should be integrated into the cached affine matrix if ( camera_ && !(grid_ && grid_.active) ) { BOOL translate = (anchorPointInPixels_.x != 0.0f || anchorPointInPixels_.y != 0.0f); if( translate ) ccglTranslate(RENDER_IN_SUBPIXEL(anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(anchorPointInPixels_.y), 0); [camera_ locate]; if( translate ) ccglTranslate(RENDER_IN_SUBPIXEL(-anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(-anchorPointInPixels_.y), 0); } // END alternative #else // BEGIN original implementation // // translate if ( isRelativeAnchorPoint_ && (anchorPointInPixels_.x != 0 || anchorPointInPixels_.y != 0 ) ) glTranslatef( RENDER_IN_SUBPIXEL(-anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(-anchorPointInPixels_.y), 0); if (anchorPointInPixels_.x != 0 || anchorPointInPixels_.y != 0) glTranslatef( RENDER_IN_SUBPIXEL(positionInPixels_.x + anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(positionInPixels_.y + anchorPointInPixels_.y), vertexZ_); else if ( positionInPixels_.x !=0 || positionInPixels_.y !=0 || vertexZ_ != 0) glTranslatef( RENDER_IN_SUBPIXEL(positionInPixels_.x), RENDER_IN_SUBPIXEL(positionInPixels_.y), vertexZ_ ); // rotate if (rotation_ != 0.0f ) glRotatef( -rotation_, 0.0f, 0.0f, 1.0f ); // skew if ( (skewX_ != 0.0f) || (skewY_ != 0.0f) ) { CGAffineTransform skewMatrix = CGAffineTransformMake( 1.0f, tanf(CC_DEGREES_TO_RADIANS(skewY_)), tanf(CC_DEGREES_TO_RADIANS(skewX_)), 1.0f, 0.0f, 0.0f ); GLfloat glMatrix[16]; CGAffineToGL(&skewMatrix, glMatrix); glMultMatrixf(glMatrix); } // scale if (scaleX_ != 1.0f || scaleY_ != 1.0f) glScalef( scaleX_, scaleY_, 1.0f ); if ( camera_ && !(grid_ && grid_.active) ) [camera_ locate]; // restore and re-position point if (anchorPointInPixels_.x != 0.0f || anchorPointInPixels_.y != 0.0f) glTranslatef(RENDER_IN_SUBPIXEL(-anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(-anchorPointInPixels_.y), 0); // // END original implementation #endif } #pragma mark CCNode SceneManagement -(void) onEnter { [children_ makeObjectsPerformSelector:@selector(onEnter)]; [self resumeSchedulerAndActions]; isRunning_ = YES; } -(void) onEnterTransitionDidFinish { [children_ makeObjectsPerformSelector:@selector(onEnterTransitionDidFinish)]; } -(void) onExit { [self pauseSchedulerAndActions]; isRunning_ = NO; [children_ makeObjectsPerformSelector:@selector(onExit)]; } #pragma mark CCNode Actions -(CCAction*) runAction:(CCAction*) action { NSAssert( action != nil, @"Argument must be non-nil"); [[CCActionManager sharedManager] addAction:action target:self paused:!isRunning_]; return action; } -(void) stopAllActions { [[CCActionManager sharedManager] removeAllActionsFromTarget:self]; } -(void) stopAction: (CCAction*) action { [[CCActionManager sharedManager] removeAction:action]; } -(void) stopActionByTag:(NSInteger)aTag { NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag"); [[CCActionManager sharedManager] removeActionByTag:aTag target:self]; } -(CCAction*) getActionByTag:(NSInteger) aTag { NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag"); return [[CCActionManager sharedManager] getActionByTag:aTag target:self]; } -(NSUInteger) numberOfRunningActions { return [[CCActionManager sharedManager] numberOfRunningActionsInTarget:self]; } #pragma mark CCNode - Scheduler -(void) scheduleUpdate { [self scheduleUpdateWithPriority:0]; } -(void) scheduleUpdateWithPriority:(NSInteger)priority { [[CCScheduler sharedScheduler] scheduleUpdateForTarget:self priority:priority paused:!isRunning_]; } -(void) unscheduleUpdate { [[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self]; } -(void) schedule:(SEL)selector { [self schedule:selector interval:0]; } -(void) schedule:(SEL)selector interval:(ccTime)interval { NSAssert( selector != nil, @"Argument must be non-nil"); NSAssert( interval >=0, @"Arguemnt must be positive"); [[CCScheduler sharedScheduler] scheduleSelector:selector forTarget:self interval:interval paused:!isRunning_]; } -(void) unschedule:(SEL)selector { // explicit nil handling if (selector == nil) return; [[CCScheduler sharedScheduler] unscheduleSelector:selector forTarget:self]; } -(void) unscheduleAllSelectors { [[CCScheduler sharedScheduler] unscheduleAllSelectorsForTarget:self]; } - (void) resumeSchedulerAndActions { [[CCScheduler sharedScheduler] resumeTarget:self]; [[CCActionManager sharedManager] resumeTarget:self]; } - (void) pauseSchedulerAndActions { [[CCScheduler sharedScheduler] pauseTarget:self]; [[CCActionManager sharedManager] pauseTarget:self]; } #pragma mark CCNode Transform - (CGAffineTransform)nodeToParentTransform { if ( isTransformDirty_ ) { transform_ = CGAffineTransformIdentity; if ( !isRelativeAnchorPoint_ && !CGPointEqualToPoint(anchorPointInPixels_, CGPointZero) ) transform_ = CGAffineTransformTranslate(transform_, anchorPointInPixels_.x, anchorPointInPixels_.y); if( ! CGPointEqualToPoint(positionInPixels_, CGPointZero) ) transform_ = CGAffineTransformTranslate(transform_, positionInPixels_.x, positionInPixels_.y); if( rotation_ != 0 ) transform_ = CGAffineTransformRotate(transform_, -CC_DEGREES_TO_RADIANS(rotation_)); if( skewX_ != 0 || skewY_ != 0 ) { // create a skewed coordinate system CGAffineTransform skew = CGAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(skewY_)), tanf(CC_DEGREES_TO_RADIANS(skewX_)), 1.0f, 0.0f, 0.0f); // apply the skew to the transform transform_ = CGAffineTransformConcat(skew, transform_); } if( ! (scaleX_ == 1 && scaleY_ == 1) ) transform_ = CGAffineTransformScale(transform_, scaleX_, scaleY_); if( ! CGPointEqualToPoint(anchorPointInPixels_, CGPointZero) ) transform_ = CGAffineTransformTranslate(transform_, -anchorPointInPixels_.x, -anchorPointInPixels_.y); isTransformDirty_ = NO; } return transform_; } - (CGAffineTransform)parentToNodeTransform { if ( isInverseDirty_ ) { inverse_ = CGAffineTransformInvert([self nodeToParentTransform]); isInverseDirty_ = NO; } return inverse_; } - (CGAffineTransform)nodeToWorldTransform { CGAffineTransform t = [self nodeToParentTransform]; for (CCNode *p = parent_; p != nil; p = p.parent) t = CGAffineTransformConcat(t, [p nodeToParentTransform]); return t; } - (CGAffineTransform)worldToNodeTransform { return CGAffineTransformInvert([self nodeToWorldTransform]); } - (CGPoint)convertToNodeSpace:(CGPoint)worldPoint { CGPoint ret; if( CC_CONTENT_SCALE_FACTOR() == 1 ) ret = CGPointApplyAffineTransform(worldPoint, [self worldToNodeTransform]); else { ret = ccpMult( worldPoint, CC_CONTENT_SCALE_FACTOR() ); ret = CGPointApplyAffineTransform(ret, [self worldToNodeTransform]); ret = ccpMult( ret, 1/CC_CONTENT_SCALE_FACTOR() ); } return ret; } - (CGPoint)convertToWorldSpace:(CGPoint)nodePoint { CGPoint ret; if( CC_CONTENT_SCALE_FACTOR() == 1 ) ret = CGPointApplyAffineTransform(nodePoint, [self nodeToWorldTransform]); else { ret = ccpMult( nodePoint, CC_CONTENT_SCALE_FACTOR() ); ret = CGPointApplyAffineTransform(ret, [self nodeToWorldTransform]); ret = ccpMult( ret, 1/CC_CONTENT_SCALE_FACTOR() ); } return ret; } - (CGPoint)convertToNodeSpaceAR:(CGPoint)worldPoint { CGPoint nodePoint = [self convertToNodeSpace:worldPoint]; CGPoint anchorInPoints; if( CC_CONTENT_SCALE_FACTOR() == 1 ) anchorInPoints = anchorPointInPixels_; else anchorInPoints = ccpMult( anchorPointInPixels_, 1/CC_CONTENT_SCALE_FACTOR() ); return ccpSub(nodePoint, anchorInPoints); } - (CGPoint)convertToWorldSpaceAR:(CGPoint)nodePoint { CGPoint anchorInPoints; if( CC_CONTENT_SCALE_FACTOR() == 1 ) anchorInPoints = anchorPointInPixels_; else anchorInPoints = ccpMult( anchorPointInPixels_, 1/CC_CONTENT_SCALE_FACTOR() ); nodePoint = ccpAdd(nodePoint, anchorInPoints); return [self convertToWorldSpace:nodePoint]; } - (CGPoint)convertToWindowSpace:(CGPoint)nodePoint { CGPoint worldPoint = [self convertToWorldSpace:nodePoint]; return [[CCDirector sharedDirector] convertToUI:worldPoint]; } // convenience methods which take a UITouch instead of CGPoint #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - (CGPoint)convertTouchToNodeSpace:(UITouch *)touch { CGPoint point = [touch locationInView: [touch view]]; point = [[CCDirector sharedDirector] convertToGL: point]; return [self convertToNodeSpace:point]; } - (CGPoint)convertTouchToNodeSpaceAR:(UITouch *)touch { CGPoint point = [touch locationInView: [touch view]]; point = [[CCDirector sharedDirector] convertToGL: point]; return [self convertToNodeSpaceAR:point]; } #endif // __IPHONE_OS_VERSION_MAX_ALLOWED @end