From 6440ee09a59c40706c35780eda04812d4034b3e3 Mon Sep 17 00:00:00 2001 From: Stepan Generalov Date: Thu, 14 Apr 2011 07:09:15 +0400 Subject: [PATCH] Transformed into cocos2d 1.0 ios template project. --- CCMenuAdvanced.xcodeproj/project.pbxproj | 1385 ++++++++++++++ CCMenuAdvanced_Prefix.pch | 8 + CCMenuAdvanced.h => Classes/CCMenuAdvanced.h | 0 CCMenuAdvanced.m => Classes/CCMenuAdvanced.m | 0 Classes/CCMenuAdvancedAppDelegate.h | 20 + Classes/CCMenuAdvancedAppDelegate.m | 159 ++ Classes/GameConfig.h | 45 + Classes/HelloWorldLayer.h | 21 + Classes/HelloWorldLayer.m | 63 + Classes/RootViewController.h | 16 + Classes/RootViewController.m | 152 ++ LICENSE.cocos2d | 23 + LICENSE.cocosdenshion | 21 + Resources/Default.png | Bin 0 -> 30635 bytes Resources/Icon-72.png | Bin 0 -> 11414 bytes Resources/Icon-Small-50.png | Bin 0 -> 7979 bytes Resources/Icon-Small.png | Bin 0 -> 4956 bytes Resources/Icon-Small@2x.png | Bin 0 -> 9465 bytes Resources/Icon.png | Bin 0 -> 9327 bytes Resources/Icon@2x.png | Bin 0 -> 21365 bytes Resources/Info.plist | 48 + Resources/fps_images.png | Bin 0 -> 6203 bytes Resources/iTunesArtwork | Bin 0 -> 61982 bytes libs/CocosDenshion/CDAudioManager.h | 243 +++ libs/CocosDenshion/CDAudioManager.m | 887 +++++++++ libs/CocosDenshion/CDConfig.h | 60 + libs/CocosDenshion/CDOpenALSupport.h | 77 + libs/CocosDenshion/CDOpenALSupport.m | 246 +++ libs/CocosDenshion/CocosDenshion.h | 440 +++++ libs/CocosDenshion/CocosDenshion.m | 1598 +++++++++++++++++ libs/CocosDenshion/SimpleAudioEngine.h | 90 + libs/CocosDenshion/SimpleAudioEngine.m | 220 +++ libs/FontLabel/FontLabel.h | 44 + libs/FontLabel/FontLabel.m | 195 ++ libs/FontLabel/FontLabelStringDrawing.h | 69 + libs/FontLabel/FontLabelStringDrawing.m | 892 +++++++++ libs/FontLabel/FontManager.h | 85 + libs/FontLabel/FontManager.m | 123 ++ libs/FontLabel/ZAttributedString.h | 77 + libs/FontLabel/ZAttributedString.m | 596 ++++++ libs/FontLabel/ZAttributedStringPrivate.h | 24 + libs/FontLabel/ZFont.h | 47 + libs/FontLabel/ZFont.m | 170 ++ libs/README | 1 + libs/TouchJSON/CDataScanner.h | 71 + libs/TouchJSON/CDataScanner.m | 340 ++++ .../Extensions/CDataScanner_Extensions.h | 40 + .../Extensions/CDataScanner_Extensions.m | 135 ++ .../Extensions/NSDictionary_JSONExtensions.h | 37 + .../Extensions/NSDictionary_JSONExtensions.m | 47 + libs/TouchJSON/JSON/CJSONDeserializer.h | 63 + libs/TouchJSON/JSON/CJSONDeserializer.m | 161 ++ libs/TouchJSON/JSON/CJSONScanner.h | 95 + libs/TouchJSON/JSON/CJSONScanner.m | 676 +++++++ libs/TouchJSON/JSON/CJSONSerializer.h | 53 + libs/TouchJSON/JSON/CJSONSerializer.m | 342 ++++ libs/TouchJSON/JSON/JSONRepresentation.h | 18 + libs/cocos2d/CCAction.h | 194 ++ libs/cocos2d/CCAction.m | 361 ++++ libs/cocos2d/CCActionCamera.h | 72 + libs/cocos2d/CCActionCamera.m | 146 ++ libs/cocos2d/CCActionEase.h | 159 ++ libs/cocos2d/CCActionEase.m | 534 ++++++ libs/cocos2d/CCActionGrid.h | 165 ++ libs/cocos2d/CCActionGrid.m | 386 ++++ libs/cocos2d/CCActionGrid3D.h | 208 +++ libs/cocos2d/CCActionGrid3D.m | 659 +++++++ libs/cocos2d/CCActionInstant.h | 204 +++ libs/cocos2d/CCActionInstant.m | 476 +++++ libs/cocos2d/CCActionInterval.h | 392 ++++ libs/cocos2d/CCActionInterval.m | 1250 +++++++++++++ libs/cocos2d/CCActionManager.h | 109 ++ libs/cocos2d/CCActionManager.m | 344 ++++ libs/cocos2d/CCActionPageTurn3D.h | 42 + libs/cocos2d/CCActionPageTurn3D.m | 86 + libs/cocos2d/CCActionProgressTimer.h | 59 + libs/cocos2d/CCActionProgressTimer.m | 103 ++ libs/cocos2d/CCActionTiledGrid.h | 211 +++ libs/cocos2d/CCActionTiledGrid.m | 768 ++++++++ libs/cocos2d/CCActionTween.h | 62 + libs/cocos2d/CCActionTween.m | 72 + libs/cocos2d/CCAnimation.h | 135 ++ libs/cocos2d/CCAnimation.m | 152 ++ libs/cocos2d/CCAnimationCache.h | 63 + libs/cocos2d/CCAnimationCache.m | 100 ++ libs/cocos2d/CCAtlasNode.h | 86 + libs/cocos2d/CCAtlasNode.m | 205 +++ libs/cocos2d/CCBlockSupport.h | 51 + libs/cocos2d/CCBlockSupport.m | 46 + libs/cocos2d/CCCamera.h | 94 + libs/cocos2d/CCCamera.m | 130 ++ libs/cocos2d/CCConfiguration.h | 110 ++ libs/cocos2d/CCConfiguration.m | 192 ++ libs/cocos2d/CCDirector.h | 304 ++++ libs/cocos2d/CCDirector.m | 557 ++++++ libs/cocos2d/CCDrawingPrimitives.h | 91 + libs/cocos2d/CCDrawingPrimitives.m | 271 +++ libs/cocos2d/CCGrabber.h | 43 + libs/cocos2d/CCGrabber.m | 95 + libs/cocos2d/CCGrid.h | 121 ++ libs/cocos2d/CCGrid.m | 571 ++++++ libs/cocos2d/CCLabelAtlas.h | 61 + libs/cocos2d/CCLabelAtlas.m | 189 ++ libs/cocos2d/CCLabelBMFont.h | 189 ++ libs/cocos2d/CCLabelBMFont.m | 672 +++++++ libs/cocos2d/CCLabelTTF.h | 62 + libs/cocos2d/CCLabelTTF.m | 125 ++ libs/cocos2d/CCLayer.h | 292 +++ libs/cocos2d/CCLayer.m | 618 +++++++ libs/cocos2d/CCMenu.h | 92 + libs/cocos2d/CCMenu.m | 522 ++++++ libs/cocos2d/CCMenuItem.h | 362 ++++ libs/cocos2d/CCMenuItem.m | 762 ++++++++ libs/cocos2d/CCMotionStreak.h | 67 + libs/cocos2d/CCMotionStreak.m | 104 ++ libs/cocos2d/CCNode.h | 510 ++++++ libs/cocos2d/CCNode.m | 883 +++++++++ libs/cocos2d/CCParallaxNode.h | 49 + libs/cocos2d/CCParallaxNode.m | 160 ++ libs/cocos2d/CCParticleExamples.h | 110 ++ libs/cocos2d/CCParticleExamples.m | 925 ++++++++++ libs/cocos2d/CCParticleSystem.h | 444 +++++ libs/cocos2d/CCParticleSystem.m | 795 ++++++++ libs/cocos2d/CCParticleSystemPoint.h | 64 + libs/cocos2d/CCParticleSystemPoint.m | 208 +++ libs/cocos2d/CCParticleSystemQuad.h | 74 + libs/cocos2d/CCParticleSystemQuad.m | 316 ++++ libs/cocos2d/CCProgressTimer.h | 83 + libs/cocos2d/CCProgressTimer.m | 491 +++++ libs/cocos2d/CCProtocols.h | 124 ++ libs/cocos2d/CCRenderTexture.h | 108 ++ libs/cocos2d/CCRenderTexture.m | 340 ++++ libs/cocos2d/CCRibbon.h | 117 ++ libs/cocos2d/CCRibbon.m | 381 ++++ libs/cocos2d/CCScene.h | 42 + libs/cocos2d/CCScene.m | 44 + libs/cocos2d/CCScheduler.h | 193 ++ libs/cocos2d/CCScheduler.m | 641 +++++++ libs/cocos2d/CCSprite.h | 351 ++++ libs/cocos2d/CCSprite.m | 984 ++++++++++ libs/cocos2d/CCSpriteBatchNode.h | 143 ++ libs/cocos2d/CCSpriteBatchNode.m | 499 +++++ libs/cocos2d/CCSpriteFrame.h | 89 + libs/cocos2d/CCSpriteFrame.m | 108 ++ libs/cocos2d/CCSpriteFrameCache.h | 133 ++ libs/cocos2d/CCSpriteFrameCache.m | 344 ++++ libs/cocos2d/CCTMXLayer.h | 151 ++ libs/cocos2d/CCTMXLayer.m | 667 +++++++ libs/cocos2d/CCTMXObjectGroup.h | 65 + libs/cocos2d/CCTMXObjectGroup.m | 84 + libs/cocos2d/CCTMXTiledMap.h | 144 ++ libs/cocos2d/CCTMXTiledMap.m | 200 +++ libs/cocos2d/CCTMXXMLParser.h | 201 +++ libs/cocos2d/CCTMXXMLParser.m | 447 +++++ libs/cocos2d/CCTexture2D.h | 308 ++++ libs/cocos2d/CCTexture2D.m | 754 ++++++++ libs/cocos2d/CCTextureAtlas.h | 146 ++ libs/cocos2d/CCTextureAtlas.m | 368 ++++ libs/cocos2d/CCTextureCache.h | 139 ++ libs/cocos2d/CCTextureCache.m | 466 +++++ libs/cocos2d/CCTexturePVR.h | 125 ++ libs/cocos2d/CCTexturePVR.m | 406 +++++ libs/cocos2d/CCTileMapAtlas.h | 82 + libs/cocos2d/CCTileMapAtlas.m | 233 +++ libs/cocos2d/CCTransition.h | 295 +++ libs/cocos2d/CCTransition.m | 1057 +++++++++++ libs/cocos2d/CCTransitionPageTurn.h | 60 + libs/cocos2d/CCTransitionPageTurn.m | 117 ++ libs/cocos2d/CCTransitionRadial.h | 40 + libs/cocos2d/CCTransitionRadial.m | 115 ++ libs/cocos2d/Platforms/CCGL.h | 82 + libs/cocos2d/Platforms/CCNS.h | 62 + libs/cocos2d/Platforms/Mac/CCDirectorMac.h | 102 ++ libs/cocos2d/Platforms/Mac/CCDirectorMac.m | 478 +++++ .../cocos2d/Platforms/Mac/CCEventDispatcher.h | 276 +++ .../cocos2d/Platforms/Mac/CCEventDispatcher.m | 644 +++++++ libs/cocos2d/Platforms/Mac/MacGLView.h | 88 + libs/cocos2d/Platforms/Mac/MacGLView.m | 241 +++ libs/cocos2d/Platforms/Mac/MacWindow.h | 42 + libs/cocos2d/Platforms/Mac/MacWindow.m | 70 + libs/cocos2d/Platforms/iOS/CCDirectorIOS.h | 254 +++ libs/cocos2d/Platforms/iOS/CCDirectorIOS.m | 729 ++++++++ .../Platforms/iOS/CCTouchDelegateProtocol.h | 75 + .../cocos2d/Platforms/iOS/CCTouchDispatcher.h | 122 ++ .../cocos2d/Platforms/iOS/CCTouchDispatcher.m | 326 ++++ libs/cocos2d/Platforms/iOS/CCTouchHandler.h | 93 + libs/cocos2d/Platforms/iOS/CCTouchHandler.m | 135 ++ libs/cocos2d/Platforms/iOS/EAGLView.h | 155 ++ libs/cocos2d/Platforms/iOS/EAGLView.m | 342 ++++ libs/cocos2d/Platforms/iOS/ES1Renderer.h | 71 + libs/cocos2d/Platforms/iOS/ES1Renderer.m | 252 +++ libs/cocos2d/Platforms/iOS/ESRenderer.h | 53 + libs/cocos2d/Platforms/iOS/glu.c | 113 ++ libs/cocos2d/Platforms/iOS/glu.h | 29 + libs/cocos2d/Support/CCArray.h | 105 ++ libs/cocos2d/Support/CCArray.m | 270 +++ libs/cocos2d/Support/CCFileUtils.h | 61 + libs/cocos2d/Support/CCFileUtils.m | 168 ++ libs/cocos2d/Support/CCProfiling.h | 53 + libs/cocos2d/Support/CCProfiling.m | 117 ++ libs/cocos2d/Support/CGPointExtension.h | 321 ++++ libs/cocos2d/Support/CGPointExtension.m | 180 ++ libs/cocos2d/Support/OpenGL_Internal.h | 80 + libs/cocos2d/Support/TGAlib.h | 55 + libs/cocos2d/Support/TGAlib.m | 272 +++ libs/cocos2d/Support/TransformUtils.h | 37 + libs/cocos2d/Support/TransformUtils.m | 46 + libs/cocos2d/Support/ZipUtils.h | 78 + libs/cocos2d/Support/ZipUtils.m | 246 +++ libs/cocos2d/Support/base64.c | 89 + libs/cocos2d/Support/base64.h | 33 + libs/cocos2d/Support/ccCArray.h | 420 +++++ libs/cocos2d/Support/ccUtils.c | 20 + libs/cocos2d/Support/ccUtils.h | 29 + libs/cocos2d/Support/uthash.h | 972 ++++++++++ libs/cocos2d/Support/utlist.h | 490 +++++ libs/cocos2d/ccConfig.h | 281 +++ libs/cocos2d/ccMacros.h | 252 +++ libs/cocos2d/ccTypes.h | 286 +++ libs/cocos2d/cocos2d.h | 160 ++ libs/cocos2d/cocos2d.m | 33 + libs/cocoslive/CLScoreServerPost.h | 141 ++ libs/cocoslive/CLScoreServerPost.m | 332 ++++ libs/cocoslive/CLScoreServerRequest.h | 121 ++ libs/cocoslive/CLScoreServerRequest.m | 256 +++ libs/cocoslive/cocoslive.h | 42 + libs/cocoslive/cocoslive.m | 34 + main.m | 16 + 228 files changed, 52172 insertions(+) create mode 100755 CCMenuAdvanced.xcodeproj/project.pbxproj create mode 100755 CCMenuAdvanced_Prefix.pch rename CCMenuAdvanced.h => Classes/CCMenuAdvanced.h (100%) rename CCMenuAdvanced.m => Classes/CCMenuAdvanced.m (100%) create mode 100755 Classes/CCMenuAdvancedAppDelegate.h create mode 100755 Classes/CCMenuAdvancedAppDelegate.m create mode 100644 Classes/GameConfig.h create mode 100644 Classes/HelloWorldLayer.h create mode 100755 Classes/HelloWorldLayer.m create mode 100644 Classes/RootViewController.h create mode 100644 Classes/RootViewController.m create mode 100644 LICENSE.cocos2d create mode 100644 LICENSE.cocosdenshion create mode 100644 Resources/Default.png create mode 100644 Resources/Icon-72.png create mode 100644 Resources/Icon-Small-50.png create mode 100644 Resources/Icon-Small.png create mode 100644 Resources/Icon-Small@2x.png create mode 100644 Resources/Icon.png create mode 100644 Resources/Icon@2x.png create mode 100644 Resources/Info.plist create mode 100644 Resources/fps_images.png create mode 100644 Resources/iTunesArtwork create mode 100644 libs/CocosDenshion/CDAudioManager.h create mode 100644 libs/CocosDenshion/CDAudioManager.m create mode 100644 libs/CocosDenshion/CDConfig.h create mode 100644 libs/CocosDenshion/CDOpenALSupport.h create mode 100644 libs/CocosDenshion/CDOpenALSupport.m create mode 100644 libs/CocosDenshion/CocosDenshion.h create mode 100644 libs/CocosDenshion/CocosDenshion.m create mode 100644 libs/CocosDenshion/SimpleAudioEngine.h create mode 100644 libs/CocosDenshion/SimpleAudioEngine.m create mode 100644 libs/FontLabel/FontLabel.h create mode 100644 libs/FontLabel/FontLabel.m create mode 100644 libs/FontLabel/FontLabelStringDrawing.h create mode 100644 libs/FontLabel/FontLabelStringDrawing.m create mode 100644 libs/FontLabel/FontManager.h create mode 100644 libs/FontLabel/FontManager.m create mode 100644 libs/FontLabel/ZAttributedString.h create mode 100644 libs/FontLabel/ZAttributedString.m create mode 100644 libs/FontLabel/ZAttributedStringPrivate.h create mode 100644 libs/FontLabel/ZFont.h create mode 100644 libs/FontLabel/ZFont.m create mode 100644 libs/README create mode 100644 libs/TouchJSON/CDataScanner.h create mode 100644 libs/TouchJSON/CDataScanner.m create mode 100644 libs/TouchJSON/Extensions/CDataScanner_Extensions.h create mode 100644 libs/TouchJSON/Extensions/CDataScanner_Extensions.m create mode 100644 libs/TouchJSON/Extensions/NSDictionary_JSONExtensions.h create mode 100644 libs/TouchJSON/Extensions/NSDictionary_JSONExtensions.m create mode 100644 libs/TouchJSON/JSON/CJSONDeserializer.h create mode 100644 libs/TouchJSON/JSON/CJSONDeserializer.m create mode 100644 libs/TouchJSON/JSON/CJSONScanner.h create mode 100644 libs/TouchJSON/JSON/CJSONScanner.m create mode 100644 libs/TouchJSON/JSON/CJSONSerializer.h create mode 100644 libs/TouchJSON/JSON/CJSONSerializer.m create mode 100644 libs/TouchJSON/JSON/JSONRepresentation.h create mode 100644 libs/cocos2d/CCAction.h create mode 100644 libs/cocos2d/CCAction.m create mode 100644 libs/cocos2d/CCActionCamera.h create mode 100644 libs/cocos2d/CCActionCamera.m create mode 100644 libs/cocos2d/CCActionEase.h create mode 100644 libs/cocos2d/CCActionEase.m create mode 100644 libs/cocos2d/CCActionGrid.h create mode 100644 libs/cocos2d/CCActionGrid.m create mode 100644 libs/cocos2d/CCActionGrid3D.h create mode 100644 libs/cocos2d/CCActionGrid3D.m create mode 100644 libs/cocos2d/CCActionInstant.h create mode 100644 libs/cocos2d/CCActionInstant.m create mode 100644 libs/cocos2d/CCActionInterval.h create mode 100644 libs/cocos2d/CCActionInterval.m create mode 100644 libs/cocos2d/CCActionManager.h create mode 100644 libs/cocos2d/CCActionManager.m create mode 100644 libs/cocos2d/CCActionPageTurn3D.h create mode 100644 libs/cocos2d/CCActionPageTurn3D.m create mode 100644 libs/cocos2d/CCActionProgressTimer.h create mode 100644 libs/cocos2d/CCActionProgressTimer.m create mode 100644 libs/cocos2d/CCActionTiledGrid.h create mode 100644 libs/cocos2d/CCActionTiledGrid.m create mode 100644 libs/cocos2d/CCActionTween.h create mode 100644 libs/cocos2d/CCActionTween.m create mode 100644 libs/cocos2d/CCAnimation.h create mode 100644 libs/cocos2d/CCAnimation.m create mode 100644 libs/cocos2d/CCAnimationCache.h create mode 100644 libs/cocos2d/CCAnimationCache.m create mode 100644 libs/cocos2d/CCAtlasNode.h create mode 100644 libs/cocos2d/CCAtlasNode.m create mode 100644 libs/cocos2d/CCBlockSupport.h create mode 100644 libs/cocos2d/CCBlockSupport.m create mode 100644 libs/cocos2d/CCCamera.h create mode 100644 libs/cocos2d/CCCamera.m create mode 100644 libs/cocos2d/CCConfiguration.h create mode 100644 libs/cocos2d/CCConfiguration.m create mode 100644 libs/cocos2d/CCDirector.h create mode 100644 libs/cocos2d/CCDirector.m create mode 100644 libs/cocos2d/CCDrawingPrimitives.h create mode 100644 libs/cocos2d/CCDrawingPrimitives.m create mode 100644 libs/cocos2d/CCGrabber.h create mode 100644 libs/cocos2d/CCGrabber.m create mode 100644 libs/cocos2d/CCGrid.h create mode 100644 libs/cocos2d/CCGrid.m create mode 100644 libs/cocos2d/CCLabelAtlas.h create mode 100644 libs/cocos2d/CCLabelAtlas.m create mode 100644 libs/cocos2d/CCLabelBMFont.h create mode 100644 libs/cocos2d/CCLabelBMFont.m create mode 100644 libs/cocos2d/CCLabelTTF.h create mode 100644 libs/cocos2d/CCLabelTTF.m create mode 100644 libs/cocos2d/CCLayer.h create mode 100644 libs/cocos2d/CCLayer.m create mode 100644 libs/cocos2d/CCMenu.h create mode 100644 libs/cocos2d/CCMenu.m create mode 100644 libs/cocos2d/CCMenuItem.h create mode 100644 libs/cocos2d/CCMenuItem.m create mode 100644 libs/cocos2d/CCMotionStreak.h create mode 100644 libs/cocos2d/CCMotionStreak.m create mode 100644 libs/cocos2d/CCNode.h create mode 100644 libs/cocos2d/CCNode.m create mode 100644 libs/cocos2d/CCParallaxNode.h create mode 100644 libs/cocos2d/CCParallaxNode.m create mode 100644 libs/cocos2d/CCParticleExamples.h create mode 100644 libs/cocos2d/CCParticleExamples.m create mode 100644 libs/cocos2d/CCParticleSystem.h create mode 100644 libs/cocos2d/CCParticleSystem.m create mode 100644 libs/cocos2d/CCParticleSystemPoint.h create mode 100644 libs/cocos2d/CCParticleSystemPoint.m create mode 100644 libs/cocos2d/CCParticleSystemQuad.h create mode 100644 libs/cocos2d/CCParticleSystemQuad.m create mode 100644 libs/cocos2d/CCProgressTimer.h create mode 100644 libs/cocos2d/CCProgressTimer.m create mode 100644 libs/cocos2d/CCProtocols.h create mode 100755 libs/cocos2d/CCRenderTexture.h create mode 100755 libs/cocos2d/CCRenderTexture.m create mode 100644 libs/cocos2d/CCRibbon.h create mode 100644 libs/cocos2d/CCRibbon.m create mode 100644 libs/cocos2d/CCScene.h create mode 100644 libs/cocos2d/CCScene.m create mode 100644 libs/cocos2d/CCScheduler.h create mode 100644 libs/cocos2d/CCScheduler.m create mode 100644 libs/cocos2d/CCSprite.h create mode 100644 libs/cocos2d/CCSprite.m create mode 100644 libs/cocos2d/CCSpriteBatchNode.h create mode 100644 libs/cocos2d/CCSpriteBatchNode.m create mode 100644 libs/cocos2d/CCSpriteFrame.h create mode 100644 libs/cocos2d/CCSpriteFrame.m create mode 100644 libs/cocos2d/CCSpriteFrameCache.h create mode 100644 libs/cocos2d/CCSpriteFrameCache.m create mode 100644 libs/cocos2d/CCTMXLayer.h create mode 100644 libs/cocos2d/CCTMXLayer.m create mode 100644 libs/cocos2d/CCTMXObjectGroup.h create mode 100644 libs/cocos2d/CCTMXObjectGroup.m create mode 100644 libs/cocos2d/CCTMXTiledMap.h create mode 100644 libs/cocos2d/CCTMXTiledMap.m create mode 100644 libs/cocos2d/CCTMXXMLParser.h create mode 100644 libs/cocos2d/CCTMXXMLParser.m create mode 100644 libs/cocos2d/CCTexture2D.h create mode 100644 libs/cocos2d/CCTexture2D.m create mode 100644 libs/cocos2d/CCTextureAtlas.h create mode 100644 libs/cocos2d/CCTextureAtlas.m create mode 100644 libs/cocos2d/CCTextureCache.h create mode 100644 libs/cocos2d/CCTextureCache.m create mode 100644 libs/cocos2d/CCTexturePVR.h create mode 100644 libs/cocos2d/CCTexturePVR.m create mode 100644 libs/cocos2d/CCTileMapAtlas.h create mode 100644 libs/cocos2d/CCTileMapAtlas.m create mode 100644 libs/cocos2d/CCTransition.h create mode 100644 libs/cocos2d/CCTransition.m create mode 100644 libs/cocos2d/CCTransitionPageTurn.h create mode 100644 libs/cocos2d/CCTransitionPageTurn.m create mode 100644 libs/cocos2d/CCTransitionRadial.h create mode 100644 libs/cocos2d/CCTransitionRadial.m create mode 100644 libs/cocos2d/Platforms/CCGL.h create mode 100644 libs/cocos2d/Platforms/CCNS.h create mode 100644 libs/cocos2d/Platforms/Mac/CCDirectorMac.h create mode 100644 libs/cocos2d/Platforms/Mac/CCDirectorMac.m create mode 100644 libs/cocos2d/Platforms/Mac/CCEventDispatcher.h create mode 100644 libs/cocos2d/Platforms/Mac/CCEventDispatcher.m create mode 100644 libs/cocos2d/Platforms/Mac/MacGLView.h create mode 100644 libs/cocos2d/Platforms/Mac/MacGLView.m create mode 100644 libs/cocos2d/Platforms/Mac/MacWindow.h create mode 100644 libs/cocos2d/Platforms/Mac/MacWindow.m create mode 100755 libs/cocos2d/Platforms/iOS/CCDirectorIOS.h create mode 100755 libs/cocos2d/Platforms/iOS/CCDirectorIOS.m create mode 100644 libs/cocos2d/Platforms/iOS/CCTouchDelegateProtocol.h create mode 100644 libs/cocos2d/Platforms/iOS/CCTouchDispatcher.h create mode 100644 libs/cocos2d/Platforms/iOS/CCTouchDispatcher.m create mode 100644 libs/cocos2d/Platforms/iOS/CCTouchHandler.h create mode 100644 libs/cocos2d/Platforms/iOS/CCTouchHandler.m create mode 100755 libs/cocos2d/Platforms/iOS/EAGLView.h create mode 100755 libs/cocos2d/Platforms/iOS/EAGLView.m create mode 100755 libs/cocos2d/Platforms/iOS/ES1Renderer.h create mode 100755 libs/cocos2d/Platforms/iOS/ES1Renderer.m create mode 100755 libs/cocos2d/Platforms/iOS/ESRenderer.h create mode 100755 libs/cocos2d/Platforms/iOS/glu.c create mode 100644 libs/cocos2d/Platforms/iOS/glu.h create mode 100644 libs/cocos2d/Support/CCArray.h create mode 100644 libs/cocos2d/Support/CCArray.m create mode 100644 libs/cocos2d/Support/CCFileUtils.h create mode 100644 libs/cocos2d/Support/CCFileUtils.m create mode 100644 libs/cocos2d/Support/CCProfiling.h create mode 100644 libs/cocos2d/Support/CCProfiling.m create mode 100644 libs/cocos2d/Support/CGPointExtension.h create mode 100644 libs/cocos2d/Support/CGPointExtension.m create mode 100644 libs/cocos2d/Support/OpenGL_Internal.h create mode 100644 libs/cocos2d/Support/TGAlib.h create mode 100644 libs/cocos2d/Support/TGAlib.m create mode 100644 libs/cocos2d/Support/TransformUtils.h create mode 100644 libs/cocos2d/Support/TransformUtils.m create mode 100644 libs/cocos2d/Support/ZipUtils.h create mode 100644 libs/cocos2d/Support/ZipUtils.m create mode 100644 libs/cocos2d/Support/base64.c create mode 100644 libs/cocos2d/Support/base64.h create mode 100644 libs/cocos2d/Support/ccCArray.h create mode 100644 libs/cocos2d/Support/ccUtils.c create mode 100644 libs/cocos2d/Support/ccUtils.h create mode 100644 libs/cocos2d/Support/uthash.h create mode 100644 libs/cocos2d/Support/utlist.h create mode 100644 libs/cocos2d/ccConfig.h create mode 100644 libs/cocos2d/ccMacros.h create mode 100644 libs/cocos2d/ccTypes.h create mode 100644 libs/cocos2d/cocos2d.h create mode 100644 libs/cocos2d/cocos2d.m create mode 100644 libs/cocoslive/CLScoreServerPost.h create mode 100644 libs/cocoslive/CLScoreServerPost.m create mode 100644 libs/cocoslive/CLScoreServerRequest.h create mode 100644 libs/cocoslive/CLScoreServerRequest.m create mode 100644 libs/cocoslive/cocoslive.h create mode 100644 libs/cocoslive/cocoslive.m create mode 100755 main.m diff --git a/CCMenuAdvanced.xcodeproj/project.pbxproj b/CCMenuAdvanced.xcodeproj/project.pbxproj new file mode 100755 index 0000000..41229a0 --- /dev/null +++ b/CCMenuAdvanced.xcodeproj/project.pbxproj @@ -0,0 +1,1385 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; }; + 1F3B9A2D0EF2145700286867 /* CCMenuAdvancedAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F3B9A2B0EF2145700286867 /* CCMenuAdvancedAppDelegate.m */; }; + 504DFBCE10AF12E9006D82FE /* CLScoreServerPost.h in Headers */ = {isa = PBXBuildFile; fileRef = 504DFBC810AF12E9006D82FE /* CLScoreServerPost.h */; }; + 504DFBCF10AF12E9006D82FE /* CLScoreServerPost.m in Sources */ = {isa = PBXBuildFile; fileRef = 504DFBC910AF12E9006D82FE /* CLScoreServerPost.m */; }; + 504DFBD010AF12E9006D82FE /* CLScoreServerRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 504DFBCA10AF12E9006D82FE /* CLScoreServerRequest.h */; }; + 504DFBD110AF12E9006D82FE /* CLScoreServerRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 504DFBCB10AF12E9006D82FE /* CLScoreServerRequest.m */; }; + 504DFBD210AF12E9006D82FE /* cocoslive.h in Headers */ = {isa = PBXBuildFile; fileRef = 504DFBCC10AF12E9006D82FE /* cocoslive.h */; }; + 504DFBD310AF12E9006D82FE /* cocoslive.m in Sources */ = {isa = PBXBuildFile; fileRef = 504DFBCD10AF12E9006D82FE /* cocoslive.m */; }; + 505574581045D68500A31725 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 506EDBA4102F4C9F00A389B3 /* AVFoundation.framework */; }; + 505574591045D68500A31725 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC6640020F83B3EA000B3E49 /* AudioToolbox.framework */; }; + 5055745A1045D68500A31725 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCCBF1B60F6022AE0040855A /* CoreGraphics.framework */; }; + 5055745B1045D68500A31725 /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC6640040F83B3EA000B3E49 /* OpenAL.framework */; }; + 5055745C1045D68500A31725 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCCBF1BA0F6022AE0040855A /* OpenGLES.framework */; }; + 5055745D1045D68500A31725 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCCBF1BC0F6022AE0040855A /* QuartzCore.framework */; }; + 5055745E1045D69D00A31725 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 506EDB87102F4C4000A389B3 /* libz.dylib */; }; + 50674853107A3AF30090963A /* ZAttributedString.h in Headers */ = {isa = PBXBuildFile; fileRef = 50674850107A3AF30090963A /* ZAttributedString.h */; }; + 50674854107A3AF30090963A /* ZAttributedString.m in Sources */ = {isa = PBXBuildFile; fileRef = 50674851107A3AF30090963A /* ZAttributedString.m */; }; + 50674855107A3AF30090963A /* ZAttributedStringPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 50674852107A3AF30090963A /* ZAttributedStringPrivate.h */; }; + 506EDB88102F4C4000A389B3 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 506EDB87102F4C4000A389B3 /* libz.dylib */; }; + 506EDBA5102F4C9F00A389B3 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 506EDBA4102F4C9F00A389B3 /* AVFoundation.framework */; }; + 506EDC31102F528A00A389B3 /* HelloWorldLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EDC30102F528A00A389B3 /* HelloWorldLayer.m */; }; + 506EE1A91030508200A389B3 /* libcocos2d libraries.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 506EE05E10304ED200A389B3 /* libcocos2d libraries.a */; }; + 507ED63C11C638C6002ED3FC /* CDAudioManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 507ED63311C638C6002ED3FC /* CDAudioManager.h */; }; + 507ED63D11C638C6002ED3FC /* CDAudioManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 507ED63411C638C6002ED3FC /* CDAudioManager.m */; }; + 507ED63E11C638C6002ED3FC /* CDConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 507ED63511C638C6002ED3FC /* CDConfig.h */; }; + 507ED63F11C638C6002ED3FC /* CDOpenALSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 507ED63611C638C6002ED3FC /* CDOpenALSupport.h */; }; + 507ED64011C638C6002ED3FC /* CDOpenALSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 507ED63711C638C6002ED3FC /* CDOpenALSupport.m */; }; + 507ED64111C638C6002ED3FC /* CocosDenshion.h in Headers */ = {isa = PBXBuildFile; fileRef = 507ED63811C638C6002ED3FC /* CocosDenshion.h */; }; + 507ED64211C638C6002ED3FC /* CocosDenshion.m in Sources */ = {isa = PBXBuildFile; fileRef = 507ED63911C638C6002ED3FC /* CocosDenshion.m */; }; + 507ED64311C638C6002ED3FC /* SimpleAudioEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = 507ED63A11C638C6002ED3FC /* SimpleAudioEngine.h */; }; + 507ED64411C638C6002ED3FC /* SimpleAudioEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 507ED63B11C638C6002ED3FC /* SimpleAudioEngine.m */; }; + 50F4132A106926B2002A0D5E /* FontLabel.h in Headers */ = {isa = PBXBuildFile; fileRef = 50F41307106926B2002A0D5E /* FontLabel.h */; }; + 50F4132B106926B2002A0D5E /* FontLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 50F41308106926B2002A0D5E /* FontLabel.m */; }; + 50F4132C106926B2002A0D5E /* FontLabelStringDrawing.h in Headers */ = {isa = PBXBuildFile; fileRef = 50F41309106926B2002A0D5E /* FontLabelStringDrawing.h */; }; + 50F4132D106926B2002A0D5E /* FontLabelStringDrawing.m in Sources */ = {isa = PBXBuildFile; fileRef = 50F4130A106926B2002A0D5E /* FontLabelStringDrawing.m */; }; + 50F4132E106926B2002A0D5E /* FontManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 50F4130B106926B2002A0D5E /* FontManager.h */; }; + 50F4132F106926B2002A0D5E /* FontManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 50F4130C106926B2002A0D5E /* FontManager.m */; }; + 50F41332106926B2002A0D5E /* ZFont.h in Headers */ = {isa = PBXBuildFile; fileRef = 50F4130F106926B2002A0D5E /* ZFont.h */; }; + 50F41333106926B2002A0D5E /* ZFont.m in Sources */ = {isa = PBXBuildFile; fileRef = 50F41310106926B2002A0D5E /* ZFont.m */; }; + 50F414F01069373D002A0D5E /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 50F414EC1069373D002A0D5E /* Default.png */; }; + 50F414F11069373D002A0D5E /* fps_images.png in Resources */ = {isa = PBXBuildFile; fileRef = 50F414ED1069373D002A0D5E /* fps_images.png */; }; + 50F414F21069373D002A0D5E /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 50F414EE1069373D002A0D5E /* Icon.png */; }; + 6D78CB7813569C68007F0032 /* CCMenuAdvanced.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D78CB7713569C68007F0032 /* CCMenuAdvanced.m */; }; + DC6640030F83B3EA000B3E49 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC6640020F83B3EA000B3E49 /* AudioToolbox.framework */; }; + DC6640050F83B3EA000B3E49 /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC6640040F83B3EA000B3E49 /* OpenAL.framework */; }; + DCCBF1B70F6022AE0040855A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCCBF1B60F6022AE0040855A /* CoreGraphics.framework */; }; + DCCBF1B90F6022AE0040855A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCCBF1B80F6022AE0040855A /* Foundation.framework */; }; + DCCBF1BB0F6022AE0040855A /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCCBF1BA0F6022AE0040855A /* OpenGLES.framework */; }; + DCCBF1BD0F6022AE0040855A /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCCBF1BC0F6022AE0040855A /* QuartzCore.framework */; }; + DCCBF1BF0F6022AE0040855A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCCBF1BE0F6022AE0040855A /* UIKit.framework */; }; + E02BB504126CA50F006E46A2 /* Icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E02BB4FE126CA50F006E46A2 /* Icon@2x.png */; }; + E02BB505126CA50F006E46A2 /* Icon-Small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E02BB4FF126CA50F006E46A2 /* Icon-Small@2x.png */; }; + E02BB506126CA50F006E46A2 /* Icon-Small.png in Resources */ = {isa = PBXBuildFile; fileRef = E02BB500126CA50F006E46A2 /* Icon-Small.png */; }; + E02BB507126CA50F006E46A2 /* Icon-Small-50.png in Resources */ = {isa = PBXBuildFile; fileRef = E02BB501126CA50F006E46A2 /* Icon-Small-50.png */; }; + E02BB508126CA50F006E46A2 /* Icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = E02BB502126CA50F006E46A2 /* Icon-72.png */; }; + E02BB763126CC1E0006E46A2 /* iTunesArtwork in Resources */ = {isa = PBXBuildFile; fileRef = E02BB762126CC1E0006E46A2 /* iTunesArtwork */; }; + E02BB80F126CC224006E46A2 /* CCAction.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB765126CC223006E46A2 /* CCAction.h */; }; + E02BB810126CC224006E46A2 /* CCAction.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB766126CC223006E46A2 /* CCAction.m */; }; + E02BB811126CC224006E46A2 /* CCActionCamera.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB767126CC223006E46A2 /* CCActionCamera.h */; }; + E02BB812126CC224006E46A2 /* CCActionCamera.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB768126CC223006E46A2 /* CCActionCamera.m */; }; + E02BB813126CC224006E46A2 /* CCActionEase.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB769126CC223006E46A2 /* CCActionEase.h */; }; + E02BB814126CC224006E46A2 /* CCActionEase.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB76A126CC223006E46A2 /* CCActionEase.m */; }; + E02BB815126CC224006E46A2 /* CCActionGrid.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB76B126CC223006E46A2 /* CCActionGrid.h */; }; + E02BB816126CC224006E46A2 /* CCActionGrid.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB76C126CC223006E46A2 /* CCActionGrid.m */; }; + E02BB817126CC224006E46A2 /* CCActionGrid3D.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB76D126CC223006E46A2 /* CCActionGrid3D.h */; }; + E02BB818126CC224006E46A2 /* CCActionGrid3D.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB76E126CC223006E46A2 /* CCActionGrid3D.m */; }; + E02BB819126CC224006E46A2 /* CCActionInstant.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB76F126CC223006E46A2 /* CCActionInstant.h */; }; + E02BB81A126CC224006E46A2 /* CCActionInstant.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB770126CC223006E46A2 /* CCActionInstant.m */; }; + E02BB81B126CC224006E46A2 /* CCActionInterval.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB771126CC223006E46A2 /* CCActionInterval.h */; }; + E02BB81C126CC224006E46A2 /* CCActionInterval.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB772126CC223006E46A2 /* CCActionInterval.m */; }; + E02BB81D126CC224006E46A2 /* CCActionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB773126CC223006E46A2 /* CCActionManager.h */; }; + E02BB81E126CC224006E46A2 /* CCActionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB774126CC223006E46A2 /* CCActionManager.m */; }; + E02BB81F126CC224006E46A2 /* CCActionPageTurn3D.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB775126CC223006E46A2 /* CCActionPageTurn3D.h */; }; + E02BB820126CC224006E46A2 /* CCActionPageTurn3D.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB776126CC223006E46A2 /* CCActionPageTurn3D.m */; }; + E02BB821126CC224006E46A2 /* CCActionProgressTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB777126CC223006E46A2 /* CCActionProgressTimer.h */; }; + E02BB822126CC224006E46A2 /* CCActionProgressTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB778126CC223006E46A2 /* CCActionProgressTimer.m */; }; + E02BB823126CC224006E46A2 /* CCActionTiledGrid.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB779126CC223006E46A2 /* CCActionTiledGrid.h */; }; + E02BB824126CC224006E46A2 /* CCActionTiledGrid.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB77A126CC223006E46A2 /* CCActionTiledGrid.m */; }; + E02BB825126CC224006E46A2 /* CCActionTween.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB77B126CC223006E46A2 /* CCActionTween.h */; }; + E02BB826126CC224006E46A2 /* CCActionTween.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB77C126CC223006E46A2 /* CCActionTween.m */; }; + E02BB827126CC224006E46A2 /* CCAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB77D126CC223006E46A2 /* CCAnimation.h */; }; + E02BB828126CC224006E46A2 /* CCAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB77E126CC223006E46A2 /* CCAnimation.m */; }; + E02BB829126CC224006E46A2 /* CCAnimationCache.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB77F126CC223006E46A2 /* CCAnimationCache.h */; }; + E02BB82A126CC224006E46A2 /* CCAnimationCache.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB780126CC223006E46A2 /* CCAnimationCache.m */; }; + E02BB82B126CC224006E46A2 /* CCAtlasNode.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB781126CC223006E46A2 /* CCAtlasNode.h */; }; + E02BB82C126CC224006E46A2 /* CCAtlasNode.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB782126CC223006E46A2 /* CCAtlasNode.m */; }; + E02BB82D126CC224006E46A2 /* CCBlockSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB783126CC223006E46A2 /* CCBlockSupport.h */; }; + E02BB82E126CC224006E46A2 /* CCBlockSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB784126CC223006E46A2 /* CCBlockSupport.m */; }; + E02BB82F126CC224006E46A2 /* CCCamera.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB785126CC223006E46A2 /* CCCamera.h */; }; + E02BB830126CC224006E46A2 /* CCCamera.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB786126CC223006E46A2 /* CCCamera.m */; }; + E02BB833126CC224006E46A2 /* ccConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB789126CC223006E46A2 /* ccConfig.h */; }; + E02BB834126CC224006E46A2 /* CCConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB78A126CC223006E46A2 /* CCConfiguration.h */; }; + E02BB835126CC224006E46A2 /* CCConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB78B126CC223006E46A2 /* CCConfiguration.m */; }; + E02BB836126CC224006E46A2 /* CCDirector.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB78C126CC223006E46A2 /* CCDirector.h */; }; + E02BB837126CC224006E46A2 /* CCDirector.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB78D126CC223006E46A2 /* CCDirector.m */; }; + E02BB838126CC224006E46A2 /* CCDrawingPrimitives.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB78E126CC223006E46A2 /* CCDrawingPrimitives.h */; }; + E02BB839126CC224006E46A2 /* CCDrawingPrimitives.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB78F126CC223006E46A2 /* CCDrawingPrimitives.m */; }; + E02BB83A126CC224006E46A2 /* CCGrabber.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB790126CC223006E46A2 /* CCGrabber.h */; }; + E02BB83B126CC224006E46A2 /* CCGrabber.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB791126CC223006E46A2 /* CCGrabber.m */; }; + E02BB83C126CC224006E46A2 /* CCGrid.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB792126CC223006E46A2 /* CCGrid.h */; }; + E02BB83D126CC224006E46A2 /* CCGrid.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB793126CC223006E46A2 /* CCGrid.m */; }; + E02BB83E126CC224006E46A2 /* CCLabelAtlas.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB794126CC223006E46A2 /* CCLabelAtlas.h */; }; + E02BB83F126CC224006E46A2 /* CCLabelAtlas.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB795126CC223006E46A2 /* CCLabelAtlas.m */; }; + E02BB840126CC224006E46A2 /* CCLabelBMFont.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB796126CC223006E46A2 /* CCLabelBMFont.h */; }; + E02BB841126CC224006E46A2 /* CCLabelBMFont.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB797126CC223006E46A2 /* CCLabelBMFont.m */; }; + E02BB842126CC224006E46A2 /* CCLabelTTF.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB798126CC223006E46A2 /* CCLabelTTF.h */; }; + E02BB843126CC224006E46A2 /* CCLabelTTF.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB799126CC223006E46A2 /* CCLabelTTF.m */; }; + E02BB844126CC224006E46A2 /* CCLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB79A126CC223006E46A2 /* CCLayer.h */; }; + E02BB845126CC224006E46A2 /* CCLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB79B126CC223006E46A2 /* CCLayer.m */; }; + E02BB846126CC224006E46A2 /* ccMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB79C126CC223006E46A2 /* ccMacros.h */; }; + E02BB847126CC224006E46A2 /* CCMenu.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB79D126CC223006E46A2 /* CCMenu.h */; }; + E02BB848126CC224006E46A2 /* CCMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB79E126CC223006E46A2 /* CCMenu.m */; }; + E02BB849126CC224006E46A2 /* CCMenuItem.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB79F126CC224006E46A2 /* CCMenuItem.h */; }; + E02BB84A126CC224006E46A2 /* CCMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7A0126CC224006E46A2 /* CCMenuItem.m */; }; + E02BB84B126CC224006E46A2 /* CCMotionStreak.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7A1126CC224006E46A2 /* CCMotionStreak.h */; }; + E02BB84C126CC224006E46A2 /* CCMotionStreak.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7A2126CC224006E46A2 /* CCMotionStreak.m */; }; + E02BB84D126CC224006E46A2 /* CCNode.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7A3126CC224006E46A2 /* CCNode.h */; }; + E02BB84E126CC224006E46A2 /* CCNode.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7A4126CC224006E46A2 /* CCNode.m */; }; + E02BB84F126CC224006E46A2 /* CCParallaxNode.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7A5126CC224006E46A2 /* CCParallaxNode.h */; }; + E02BB850126CC224006E46A2 /* CCParallaxNode.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7A6126CC224006E46A2 /* CCParallaxNode.m */; }; + E02BB851126CC224006E46A2 /* CCParticleExamples.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7A7126CC224006E46A2 /* CCParticleExamples.h */; }; + E02BB852126CC224006E46A2 /* CCParticleExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7A8126CC224006E46A2 /* CCParticleExamples.m */; }; + E02BB853126CC224006E46A2 /* CCParticleSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7A9126CC224006E46A2 /* CCParticleSystem.h */; }; + E02BB854126CC224006E46A2 /* CCParticleSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7AA126CC224006E46A2 /* CCParticleSystem.m */; }; + E02BB855126CC224006E46A2 /* CCParticleSystemPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7AB126CC224006E46A2 /* CCParticleSystemPoint.h */; }; + E02BB856126CC224006E46A2 /* CCParticleSystemPoint.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7AC126CC224006E46A2 /* CCParticleSystemPoint.m */; }; + E02BB857126CC224006E46A2 /* CCParticleSystemQuad.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7AD126CC224006E46A2 /* CCParticleSystemQuad.h */; }; + E02BB858126CC224006E46A2 /* CCParticleSystemQuad.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7AE126CC224006E46A2 /* CCParticleSystemQuad.m */; }; + E02BB859126CC224006E46A2 /* CCProgressTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7AF126CC224006E46A2 /* CCProgressTimer.h */; }; + E02BB85A126CC224006E46A2 /* CCProgressTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7B0126CC224006E46A2 /* CCProgressTimer.m */; }; + E02BB85B126CC224006E46A2 /* CCProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7B1126CC224006E46A2 /* CCProtocols.h */; }; + E02BB85C126CC224006E46A2 /* CCRenderTexture.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7B2126CC224006E46A2 /* CCRenderTexture.h */; }; + E02BB85D126CC224006E46A2 /* CCRenderTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7B3126CC224006E46A2 /* CCRenderTexture.m */; }; + E02BB85E126CC224006E46A2 /* CCRibbon.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7B4126CC224006E46A2 /* CCRibbon.h */; }; + E02BB85F126CC224006E46A2 /* CCRibbon.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7B5126CC224006E46A2 /* CCRibbon.m */; }; + E02BB860126CC224006E46A2 /* CCScene.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7B6126CC224006E46A2 /* CCScene.h */; }; + E02BB861126CC224006E46A2 /* CCScene.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7B7126CC224006E46A2 /* CCScene.m */; }; + E02BB862126CC224006E46A2 /* CCScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7B8126CC224006E46A2 /* CCScheduler.h */; }; + E02BB863126CC224006E46A2 /* CCScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7B9126CC224006E46A2 /* CCScheduler.m */; }; + E02BB864126CC224006E46A2 /* CCSprite.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7BA126CC224006E46A2 /* CCSprite.h */; }; + E02BB865126CC224006E46A2 /* CCSprite.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7BB126CC224006E46A2 /* CCSprite.m */; }; + E02BB866126CC224006E46A2 /* CCSpriteBatchNode.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7BC126CC224006E46A2 /* CCSpriteBatchNode.h */; }; + E02BB867126CC224006E46A2 /* CCSpriteBatchNode.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7BD126CC224006E46A2 /* CCSpriteBatchNode.m */; }; + E02BB868126CC224006E46A2 /* CCSpriteFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7BE126CC224006E46A2 /* CCSpriteFrame.h */; }; + E02BB869126CC224006E46A2 /* CCSpriteFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7BF126CC224006E46A2 /* CCSpriteFrame.m */; }; + E02BB86A126CC224006E46A2 /* CCSpriteFrameCache.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7C0126CC224006E46A2 /* CCSpriteFrameCache.h */; }; + E02BB86B126CC224006E46A2 /* CCSpriteFrameCache.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7C1126CC224006E46A2 /* CCSpriteFrameCache.m */; }; + E02BB86E126CC224006E46A2 /* CCTexture2D.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7C4126CC224006E46A2 /* CCTexture2D.h */; }; + E02BB86F126CC224006E46A2 /* CCTexture2D.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7C5126CC224006E46A2 /* CCTexture2D.m */; }; + E02BB870126CC224006E46A2 /* CCTextureAtlas.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7C6126CC224006E46A2 /* CCTextureAtlas.h */; }; + E02BB871126CC224006E46A2 /* CCTextureAtlas.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7C7126CC224006E46A2 /* CCTextureAtlas.m */; }; + E02BB872126CC224006E46A2 /* CCTextureCache.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7C8126CC224006E46A2 /* CCTextureCache.h */; }; + E02BB873126CC224006E46A2 /* CCTextureCache.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7C9126CC224006E46A2 /* CCTextureCache.m */; }; + E02BB874126CC224006E46A2 /* CCTexturePVR.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7CA126CC224006E46A2 /* CCTexturePVR.h */; }; + E02BB875126CC224006E46A2 /* CCTexturePVR.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7CB126CC224006E46A2 /* CCTexturePVR.m */; }; + E02BB876126CC224006E46A2 /* CCTileMapAtlas.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7CC126CC224006E46A2 /* CCTileMapAtlas.h */; }; + E02BB877126CC224006E46A2 /* CCTileMapAtlas.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7CD126CC224006E46A2 /* CCTileMapAtlas.m */; }; + E02BB878126CC224006E46A2 /* CCTMXLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7CE126CC224006E46A2 /* CCTMXLayer.h */; }; + E02BB879126CC224006E46A2 /* CCTMXLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7CF126CC224006E46A2 /* CCTMXLayer.m */; }; + E02BB87A126CC224006E46A2 /* CCTMXObjectGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7D0126CC224006E46A2 /* CCTMXObjectGroup.h */; }; + E02BB87B126CC224006E46A2 /* CCTMXObjectGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7D1126CC224006E46A2 /* CCTMXObjectGroup.m */; }; + E02BB87C126CC224006E46A2 /* CCTMXTiledMap.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7D2126CC224006E46A2 /* CCTMXTiledMap.h */; }; + E02BB87D126CC224006E46A2 /* CCTMXTiledMap.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7D3126CC224006E46A2 /* CCTMXTiledMap.m */; }; + E02BB87E126CC224006E46A2 /* CCTMXXMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7D4126CC224006E46A2 /* CCTMXXMLParser.h */; }; + E02BB87F126CC224006E46A2 /* CCTMXXMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7D5126CC224006E46A2 /* CCTMXXMLParser.m */; }; + E02BB880126CC224006E46A2 /* CCTransition.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7D6126CC224006E46A2 /* CCTransition.h */; }; + E02BB881126CC224006E46A2 /* CCTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7D7126CC224006E46A2 /* CCTransition.m */; }; + E02BB882126CC224006E46A2 /* CCTransitionPageTurn.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7D8126CC224006E46A2 /* CCTransitionPageTurn.h */; }; + E02BB883126CC224006E46A2 /* CCTransitionPageTurn.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7D9126CC224006E46A2 /* CCTransitionPageTurn.m */; }; + E02BB884126CC224006E46A2 /* CCTransitionRadial.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7DA126CC224006E46A2 /* CCTransitionRadial.h */; }; + E02BB885126CC224006E46A2 /* CCTransitionRadial.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7DB126CC224006E46A2 /* CCTransitionRadial.m */; }; + E02BB886126CC224006E46A2 /* ccTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7DC126CC224006E46A2 /* ccTypes.h */; }; + E02BB887126CC224006E46A2 /* cocos2d.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7DD126CC224006E46A2 /* cocos2d.h */; }; + E02BB888126CC224006E46A2 /* cocos2d.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7DE126CC224006E46A2 /* cocos2d.m */; }; + E02BB889126CC224006E46A2 /* CCGL.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7E0126CC224006E46A2 /* CCGL.h */; }; + E02BB88A126CC224006E46A2 /* CCNS.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7E1126CC224006E46A2 /* CCNS.h */; }; + E02BB88B126CC224006E46A2 /* CCDirectorIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7E3126CC224006E46A2 /* CCDirectorIOS.h */; }; + E02BB88C126CC224006E46A2 /* CCDirectorIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7E4126CC224006E46A2 /* CCDirectorIOS.m */; }; + E02BB88D126CC224006E46A2 /* CCTouchDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7E5126CC224006E46A2 /* CCTouchDelegateProtocol.h */; }; + E02BB88E126CC224006E46A2 /* CCTouchDispatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7E6126CC224006E46A2 /* CCTouchDispatcher.h */; }; + E02BB88F126CC224006E46A2 /* CCTouchDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7E7126CC224006E46A2 /* CCTouchDispatcher.m */; }; + E02BB890126CC224006E46A2 /* CCTouchHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7E8126CC224006E46A2 /* CCTouchHandler.h */; }; + E02BB891126CC224006E46A2 /* CCTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7E9126CC224006E46A2 /* CCTouchHandler.m */; }; + E02BB892126CC224006E46A2 /* EAGLView.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7EA126CC224006E46A2 /* EAGLView.h */; }; + E02BB893126CC224006E46A2 /* EAGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7EB126CC224006E46A2 /* EAGLView.m */; }; + E02BB894126CC224006E46A2 /* ES1Renderer.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7EC126CC224006E46A2 /* ES1Renderer.h */; }; + E02BB895126CC224006E46A2 /* ES1Renderer.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7ED126CC224006E46A2 /* ES1Renderer.m */; }; + E02BB896126CC224006E46A2 /* ESRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7EE126CC224006E46A2 /* ESRenderer.h */; }; + E02BB897126CC224006E46A2 /* glu.c in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7EF126CC224006E46A2 /* glu.c */; }; + E02BB898126CC224006E46A2 /* glu.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7F0126CC224006E46A2 /* glu.h */; }; + E02BB899126CC224006E46A2 /* CCDirectorMac.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7F2126CC224006E46A2 /* CCDirectorMac.h */; }; + E02BB89A126CC224006E46A2 /* CCDirectorMac.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7F3126CC224006E46A2 /* CCDirectorMac.m */; }; + E02BB89B126CC224006E46A2 /* CCEventDispatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7F4126CC224006E46A2 /* CCEventDispatcher.h */; }; + E02BB89C126CC224006E46A2 /* CCEventDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7F5126CC224006E46A2 /* CCEventDispatcher.m */; }; + E02BB89D126CC224006E46A2 /* MacGLView.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7F6126CC224006E46A2 /* MacGLView.h */; }; + E02BB89E126CC224006E46A2 /* MacGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7F7126CC224006E46A2 /* MacGLView.m */; }; + E02BB89F126CC224006E46A2 /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7F9126CC224006E46A2 /* base64.c */; }; + E02BB8A0126CC224006E46A2 /* base64.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7FA126CC224006E46A2 /* base64.h */; }; + E02BB8A1126CC224006E46A2 /* CCArray.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7FB126CC224006E46A2 /* CCArray.h */; }; + E02BB8A2126CC224006E46A2 /* CCArray.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7FC126CC224006E46A2 /* CCArray.m */; }; + E02BB8A3126CC224006E46A2 /* ccCArray.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7FD126CC224006E46A2 /* ccCArray.h */; }; + E02BB8A4126CC224006E46A2 /* CCFileUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB7FE126CC224006E46A2 /* CCFileUtils.h */; }; + E02BB8A5126CC224006E46A2 /* CCFileUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB7FF126CC224006E46A2 /* CCFileUtils.m */; }; + E02BB8A6126CC224006E46A2 /* CCProfiling.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB800126CC224006E46A2 /* CCProfiling.h */; }; + E02BB8A7126CC224006E46A2 /* CCProfiling.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB801126CC224006E46A2 /* CCProfiling.m */; }; + E02BB8A8126CC224006E46A2 /* ccUtils.c in Sources */ = {isa = PBXBuildFile; fileRef = E02BB802126CC224006E46A2 /* ccUtils.c */; }; + E02BB8A9126CC224006E46A2 /* ccUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB803126CC224006E46A2 /* ccUtils.h */; }; + E02BB8AA126CC224006E46A2 /* CGPointExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB804126CC224006E46A2 /* CGPointExtension.h */; }; + E02BB8AB126CC224006E46A2 /* CGPointExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB805126CC224006E46A2 /* CGPointExtension.m */; }; + E02BB8AC126CC224006E46A2 /* OpenGL_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB806126CC224006E46A2 /* OpenGL_Internal.h */; }; + E02BB8AD126CC224006E46A2 /* TGAlib.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB807126CC224006E46A2 /* TGAlib.h */; }; + E02BB8AE126CC224006E46A2 /* TGAlib.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB808126CC224006E46A2 /* TGAlib.m */; }; + E02BB8AF126CC224006E46A2 /* TransformUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB809126CC224006E46A2 /* TransformUtils.h */; }; + E02BB8B0126CC224006E46A2 /* TransformUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB80A126CC224006E46A2 /* TransformUtils.m */; }; + E02BB8B1126CC224006E46A2 /* uthash.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB80B126CC224006E46A2 /* uthash.h */; }; + E02BB8B2126CC224006E46A2 /* utlist.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB80C126CC224006E46A2 /* utlist.h */; }; + E02BB8B3126CC224006E46A2 /* ZipUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = E02BB80D126CC224006E46A2 /* ZipUtils.h */; }; + E02BB8B4126CC224006E46A2 /* ZipUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E02BB80E126CC224006E46A2 /* ZipUtils.m */; }; + E0ECA3B7134E5E5600E7A048 /* CDataScanner.h in Headers */ = {isa = PBXBuildFile; fileRef = E0ECA3A8134E5E5600E7A048 /* CDataScanner.h */; }; + E0ECA3B8134E5E5600E7A048 /* CDataScanner.m in Sources */ = {isa = PBXBuildFile; fileRef = E0ECA3A9134E5E5600E7A048 /* CDataScanner.m */; }; + E0ECA3B9134E5E5600E7A048 /* CDataScanner_Extensions.h in Headers */ = {isa = PBXBuildFile; fileRef = E0ECA3AB134E5E5600E7A048 /* CDataScanner_Extensions.h */; }; + E0ECA3BA134E5E5600E7A048 /* CDataScanner_Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = E0ECA3AC134E5E5600E7A048 /* CDataScanner_Extensions.m */; }; + E0ECA3BB134E5E5600E7A048 /* NSDictionary_JSONExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = E0ECA3AD134E5E5600E7A048 /* NSDictionary_JSONExtensions.h */; }; + E0ECA3BC134E5E5600E7A048 /* NSDictionary_JSONExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = E0ECA3AE134E5E5600E7A048 /* NSDictionary_JSONExtensions.m */; }; + E0ECA3BD134E5E5600E7A048 /* CJSONDeserializer.h in Headers */ = {isa = PBXBuildFile; fileRef = E0ECA3B0134E5E5600E7A048 /* CJSONDeserializer.h */; }; + E0ECA3BE134E5E5600E7A048 /* CJSONDeserializer.m in Sources */ = {isa = PBXBuildFile; fileRef = E0ECA3B1134E5E5600E7A048 /* CJSONDeserializer.m */; }; + E0ECA3BF134E5E5600E7A048 /* CJSONScanner.h in Headers */ = {isa = PBXBuildFile; fileRef = E0ECA3B2134E5E5600E7A048 /* CJSONScanner.h */; }; + E0ECA3C0134E5E5600E7A048 /* CJSONScanner.m in Sources */ = {isa = PBXBuildFile; fileRef = E0ECA3B3134E5E5600E7A048 /* CJSONScanner.m */; }; + E0ECA3C1134E5E5600E7A048 /* CJSONSerializer.h in Headers */ = {isa = PBXBuildFile; fileRef = E0ECA3B4134E5E5600E7A048 /* CJSONSerializer.h */; }; + E0ECA3C2134E5E5600E7A048 /* CJSONSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = E0ECA3B5134E5E5600E7A048 /* CJSONSerializer.m */; }; + E0ECA3C3134E5E5600E7A048 /* JSONRepresentation.h in Headers */ = {isa = PBXBuildFile; fileRef = E0ECA3B6134E5E5600E7A048 /* JSONRepresentation.h */; }; + E0F80F60120A0182005866B8 /* RootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E0F80F5F120A0182005866B8 /* RootViewController.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 506EE1A71030507B00A389B3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 506EE05D10304ED200A389B3; + remoteInfo = "cocos2d libraries"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 1D6058910D05DD3D006BFB54 /* CCMenuAdvanced.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CCMenuAdvanced.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1F3B9A2B0EF2145700286867 /* CCMenuAdvancedAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CCMenuAdvancedAppDelegate.m; path = Classes/CCMenuAdvancedAppDelegate.m; sourceTree = SOURCE_ROOT; }; + 1F3B9A2C0EF2145700286867 /* CCMenuAdvancedAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CCMenuAdvancedAppDelegate.h; path = Classes/CCMenuAdvancedAppDelegate.h; sourceTree = SOURCE_ROOT; }; + 1F3B9A820EF2151B00286867 /* CCMenuAdvanced_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCMenuAdvanced_Prefix.pch; sourceTree = SOURCE_ROOT; }; + 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 504DFBC810AF12E9006D82FE /* CLScoreServerPost.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CLScoreServerPost.h; sourceTree = ""; }; + 504DFBC910AF12E9006D82FE /* CLScoreServerPost.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CLScoreServerPost.m; sourceTree = ""; }; + 504DFBCA10AF12E9006D82FE /* CLScoreServerRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CLScoreServerRequest.h; sourceTree = ""; }; + 504DFBCB10AF12E9006D82FE /* CLScoreServerRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CLScoreServerRequest.m; sourceTree = ""; }; + 504DFBCC10AF12E9006D82FE /* cocoslive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cocoslive.h; sourceTree = ""; }; + 504DFBCD10AF12E9006D82FE /* cocoslive.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = cocoslive.m; sourceTree = ""; }; + 504DFC6810AF1739006D82FE /* LICENSE.cocos2d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE.cocos2d; sourceTree = ""; }; + 504DFC6910AF1739006D82FE /* LICENSE.cocosdenshion */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE.cocosdenshion; sourceTree = ""; }; + 50674850107A3AF30090963A /* ZAttributedString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZAttributedString.h; sourceTree = ""; }; + 50674851107A3AF30090963A /* ZAttributedString.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZAttributedString.m; sourceTree = ""; }; + 50674852107A3AF30090963A /* ZAttributedStringPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZAttributedStringPrivate.h; sourceTree = ""; }; + 506EDB87102F4C4000A389B3 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; + 506EDBA4102F4C9F00A389B3 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + 506EDC2F102F528A00A389B3 /* HelloWorldLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HelloWorldLayer.h; sourceTree = ""; }; + 506EDC30102F528A00A389B3 /* HelloWorldLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HelloWorldLayer.m; sourceTree = ""; }; + 506EE05E10304ED200A389B3 /* libcocos2d libraries.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libcocos2d libraries.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 507ED63311C638C6002ED3FC /* CDAudioManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDAudioManager.h; sourceTree = ""; }; + 507ED63411C638C6002ED3FC /* CDAudioManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDAudioManager.m; sourceTree = ""; }; + 507ED63511C638C6002ED3FC /* CDConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDConfig.h; sourceTree = ""; }; + 507ED63611C638C6002ED3FC /* CDOpenALSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDOpenALSupport.h; sourceTree = ""; }; + 507ED63711C638C6002ED3FC /* CDOpenALSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDOpenALSupport.m; sourceTree = ""; }; + 507ED63811C638C6002ED3FC /* CocosDenshion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CocosDenshion.h; sourceTree = ""; }; + 507ED63911C638C6002ED3FC /* CocosDenshion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CocosDenshion.m; sourceTree = ""; }; + 507ED63A11C638C6002ED3FC /* SimpleAudioEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleAudioEngine.h; sourceTree = ""; }; + 507ED63B11C638C6002ED3FC /* SimpleAudioEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SimpleAudioEngine.m; sourceTree = ""; }; + 50F41307106926B2002A0D5E /* FontLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontLabel.h; sourceTree = ""; }; + 50F41308106926B2002A0D5E /* FontLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FontLabel.m; sourceTree = ""; }; + 50F41309106926B2002A0D5E /* FontLabelStringDrawing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontLabelStringDrawing.h; sourceTree = ""; }; + 50F4130A106926B2002A0D5E /* FontLabelStringDrawing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FontLabelStringDrawing.m; sourceTree = ""; }; + 50F4130B106926B2002A0D5E /* FontManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontManager.h; sourceTree = ""; }; + 50F4130C106926B2002A0D5E /* FontManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FontManager.m; sourceTree = ""; }; + 50F4130F106926B2002A0D5E /* ZFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZFont.h; sourceTree = ""; }; + 50F41310106926B2002A0D5E /* ZFont.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZFont.m; sourceTree = ""; }; + 50F414EC1069373D002A0D5E /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; + 50F414ED1069373D002A0D5E /* fps_images.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = fps_images.png; sourceTree = ""; }; + 50F414EE1069373D002A0D5E /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Icon.png; sourceTree = ""; }; + 50F414EF1069373D002A0D5E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6D78CB7613569C68007F0032 /* CCMenuAdvanced.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCMenuAdvanced.h; sourceTree = ""; }; + 6D78CB7713569C68007F0032 /* CCMenuAdvanced.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCMenuAdvanced.m; sourceTree = ""; }; + DC6640020F83B3EA000B3E49 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; + DC6640040F83B3EA000B3E49 /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = System/Library/Frameworks/OpenAL.framework; sourceTree = SDKROOT; }; + DCCBF1B60F6022AE0040855A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + DCCBF1B80F6022AE0040855A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + DCCBF1BA0F6022AE0040855A /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; + DCCBF1BC0F6022AE0040855A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + DCCBF1BE0F6022AE0040855A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + E02BB4FE126CA50F006E46A2 /* Icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon@2x.png"; sourceTree = ""; }; + E02BB4FF126CA50F006E46A2 /* Icon-Small@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small@2x.png"; sourceTree = ""; }; + E02BB500126CA50F006E46A2 /* Icon-Small.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small.png"; sourceTree = ""; }; + E02BB501126CA50F006E46A2 /* Icon-Small-50.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-50.png"; sourceTree = ""; }; + E02BB502126CA50F006E46A2 /* Icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-72.png"; sourceTree = ""; }; + E02BB762126CC1E0006E46A2 /* iTunesArtwork */ = {isa = PBXFileReference; lastKnownFileType = file; path = iTunesArtwork; sourceTree = ""; }; + E02BB765126CC223006E46A2 /* CCAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCAction.h; sourceTree = ""; }; + E02BB766126CC223006E46A2 /* CCAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAction.m; sourceTree = ""; }; + E02BB767126CC223006E46A2 /* CCActionCamera.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionCamera.h; sourceTree = ""; }; + E02BB768126CC223006E46A2 /* CCActionCamera.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionCamera.m; sourceTree = ""; }; + E02BB769126CC223006E46A2 /* CCActionEase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionEase.h; sourceTree = ""; }; + E02BB76A126CC223006E46A2 /* CCActionEase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionEase.m; sourceTree = ""; }; + E02BB76B126CC223006E46A2 /* CCActionGrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionGrid.h; sourceTree = ""; }; + E02BB76C126CC223006E46A2 /* CCActionGrid.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionGrid.m; sourceTree = ""; }; + E02BB76D126CC223006E46A2 /* CCActionGrid3D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionGrid3D.h; sourceTree = ""; }; + E02BB76E126CC223006E46A2 /* CCActionGrid3D.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionGrid3D.m; sourceTree = ""; }; + E02BB76F126CC223006E46A2 /* CCActionInstant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionInstant.h; sourceTree = ""; }; + E02BB770126CC223006E46A2 /* CCActionInstant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionInstant.m; sourceTree = ""; }; + E02BB771126CC223006E46A2 /* CCActionInterval.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionInterval.h; sourceTree = ""; }; + E02BB772126CC223006E46A2 /* CCActionInterval.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionInterval.m; sourceTree = ""; }; + E02BB773126CC223006E46A2 /* CCActionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionManager.h; sourceTree = ""; }; + E02BB774126CC223006E46A2 /* CCActionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionManager.m; sourceTree = ""; }; + E02BB775126CC223006E46A2 /* CCActionPageTurn3D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionPageTurn3D.h; sourceTree = ""; }; + E02BB776126CC223006E46A2 /* CCActionPageTurn3D.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionPageTurn3D.m; sourceTree = ""; }; + E02BB777126CC223006E46A2 /* CCActionProgressTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionProgressTimer.h; sourceTree = ""; }; + E02BB778126CC223006E46A2 /* CCActionProgressTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionProgressTimer.m; sourceTree = ""; }; + E02BB779126CC223006E46A2 /* CCActionTiledGrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionTiledGrid.h; sourceTree = ""; }; + E02BB77A126CC223006E46A2 /* CCActionTiledGrid.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionTiledGrid.m; sourceTree = ""; }; + E02BB77B126CC223006E46A2 /* CCActionTween.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionTween.h; sourceTree = ""; }; + E02BB77C126CC223006E46A2 /* CCActionTween.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionTween.m; sourceTree = ""; }; + E02BB77D126CC223006E46A2 /* CCAnimation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCAnimation.h; sourceTree = ""; }; + E02BB77E126CC223006E46A2 /* CCAnimation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAnimation.m; sourceTree = ""; }; + E02BB77F126CC223006E46A2 /* CCAnimationCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCAnimationCache.h; sourceTree = ""; }; + E02BB780126CC223006E46A2 /* CCAnimationCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAnimationCache.m; sourceTree = ""; }; + E02BB781126CC223006E46A2 /* CCAtlasNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCAtlasNode.h; sourceTree = ""; }; + E02BB782126CC223006E46A2 /* CCAtlasNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAtlasNode.m; sourceTree = ""; }; + E02BB783126CC223006E46A2 /* CCBlockSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCBlockSupport.h; sourceTree = ""; }; + E02BB784126CC223006E46A2 /* CCBlockSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCBlockSupport.m; sourceTree = ""; }; + E02BB785126CC223006E46A2 /* CCCamera.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCCamera.h; sourceTree = ""; }; + E02BB786126CC223006E46A2 /* CCCamera.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCCamera.m; sourceTree = ""; }; + E02BB789126CC223006E46A2 /* ccConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccConfig.h; sourceTree = ""; }; + E02BB78A126CC223006E46A2 /* CCConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCConfiguration.h; sourceTree = ""; }; + E02BB78B126CC223006E46A2 /* CCConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCConfiguration.m; sourceTree = ""; }; + E02BB78C126CC223006E46A2 /* CCDirector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCDirector.h; sourceTree = ""; }; + E02BB78D126CC223006E46A2 /* CCDirector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCDirector.m; sourceTree = ""; }; + E02BB78E126CC223006E46A2 /* CCDrawingPrimitives.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCDrawingPrimitives.h; sourceTree = ""; }; + E02BB78F126CC223006E46A2 /* CCDrawingPrimitives.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCDrawingPrimitives.m; sourceTree = ""; }; + E02BB790126CC223006E46A2 /* CCGrabber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCGrabber.h; sourceTree = ""; }; + E02BB791126CC223006E46A2 /* CCGrabber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCGrabber.m; sourceTree = ""; }; + E02BB792126CC223006E46A2 /* CCGrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCGrid.h; sourceTree = ""; }; + E02BB793126CC223006E46A2 /* CCGrid.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCGrid.m; sourceTree = ""; }; + E02BB794126CC223006E46A2 /* CCLabelAtlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCLabelAtlas.h; sourceTree = ""; }; + E02BB795126CC223006E46A2 /* CCLabelAtlas.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCLabelAtlas.m; sourceTree = ""; }; + E02BB796126CC223006E46A2 /* CCLabelBMFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCLabelBMFont.h; sourceTree = ""; }; + E02BB797126CC223006E46A2 /* CCLabelBMFont.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCLabelBMFont.m; sourceTree = ""; }; + E02BB798126CC223006E46A2 /* CCLabelTTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCLabelTTF.h; sourceTree = ""; }; + E02BB799126CC223006E46A2 /* CCLabelTTF.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCLabelTTF.m; sourceTree = ""; }; + E02BB79A126CC223006E46A2 /* CCLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCLayer.h; sourceTree = ""; }; + E02BB79B126CC223006E46A2 /* CCLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCLayer.m; sourceTree = ""; }; + E02BB79C126CC223006E46A2 /* ccMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccMacros.h; sourceTree = ""; }; + E02BB79D126CC223006E46A2 /* CCMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCMenu.h; sourceTree = ""; }; + E02BB79E126CC223006E46A2 /* CCMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCMenu.m; sourceTree = ""; }; + E02BB79F126CC224006E46A2 /* CCMenuItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCMenuItem.h; sourceTree = ""; }; + E02BB7A0126CC224006E46A2 /* CCMenuItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCMenuItem.m; sourceTree = ""; }; + E02BB7A1126CC224006E46A2 /* CCMotionStreak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCMotionStreak.h; sourceTree = ""; }; + E02BB7A2126CC224006E46A2 /* CCMotionStreak.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCMotionStreak.m; sourceTree = ""; }; + E02BB7A3126CC224006E46A2 /* CCNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCNode.h; sourceTree = ""; }; + E02BB7A4126CC224006E46A2 /* CCNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCNode.m; sourceTree = ""; }; + E02BB7A5126CC224006E46A2 /* CCParallaxNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCParallaxNode.h; sourceTree = ""; }; + E02BB7A6126CC224006E46A2 /* CCParallaxNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCParallaxNode.m; sourceTree = ""; }; + E02BB7A7126CC224006E46A2 /* CCParticleExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCParticleExamples.h; sourceTree = ""; }; + E02BB7A8126CC224006E46A2 /* CCParticleExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCParticleExamples.m; sourceTree = ""; }; + E02BB7A9126CC224006E46A2 /* CCParticleSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCParticleSystem.h; sourceTree = ""; }; + E02BB7AA126CC224006E46A2 /* CCParticleSystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCParticleSystem.m; sourceTree = ""; }; + E02BB7AB126CC224006E46A2 /* CCParticleSystemPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCParticleSystemPoint.h; sourceTree = ""; }; + E02BB7AC126CC224006E46A2 /* CCParticleSystemPoint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCParticleSystemPoint.m; sourceTree = ""; }; + E02BB7AD126CC224006E46A2 /* CCParticleSystemQuad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCParticleSystemQuad.h; sourceTree = ""; }; + E02BB7AE126CC224006E46A2 /* CCParticleSystemQuad.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCParticleSystemQuad.m; sourceTree = ""; }; + E02BB7AF126CC224006E46A2 /* CCProgressTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCProgressTimer.h; sourceTree = ""; }; + E02BB7B0126CC224006E46A2 /* CCProgressTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCProgressTimer.m; sourceTree = ""; }; + E02BB7B1126CC224006E46A2 /* CCProtocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCProtocols.h; sourceTree = ""; }; + E02BB7B2126CC224006E46A2 /* CCRenderTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCRenderTexture.h; sourceTree = ""; }; + E02BB7B3126CC224006E46A2 /* CCRenderTexture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCRenderTexture.m; sourceTree = ""; }; + E02BB7B4126CC224006E46A2 /* CCRibbon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCRibbon.h; sourceTree = ""; }; + E02BB7B5126CC224006E46A2 /* CCRibbon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCRibbon.m; sourceTree = ""; }; + E02BB7B6126CC224006E46A2 /* CCScene.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCScene.h; sourceTree = ""; }; + E02BB7B7126CC224006E46A2 /* CCScene.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCScene.m; sourceTree = ""; }; + E02BB7B8126CC224006E46A2 /* CCScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCScheduler.h; sourceTree = ""; }; + E02BB7B9126CC224006E46A2 /* CCScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCScheduler.m; sourceTree = ""; }; + E02BB7BA126CC224006E46A2 /* CCSprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCSprite.h; sourceTree = ""; }; + E02BB7BB126CC224006E46A2 /* CCSprite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCSprite.m; sourceTree = ""; }; + E02BB7BC126CC224006E46A2 /* CCSpriteBatchNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCSpriteBatchNode.h; sourceTree = ""; }; + E02BB7BD126CC224006E46A2 /* CCSpriteBatchNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCSpriteBatchNode.m; sourceTree = ""; }; + E02BB7BE126CC224006E46A2 /* CCSpriteFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCSpriteFrame.h; sourceTree = ""; }; + E02BB7BF126CC224006E46A2 /* CCSpriteFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCSpriteFrame.m; sourceTree = ""; }; + E02BB7C0126CC224006E46A2 /* CCSpriteFrameCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCSpriteFrameCache.h; sourceTree = ""; }; + E02BB7C1126CC224006E46A2 /* CCSpriteFrameCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCSpriteFrameCache.m; sourceTree = ""; }; + E02BB7C4126CC224006E46A2 /* CCTexture2D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTexture2D.h; sourceTree = ""; }; + E02BB7C5126CC224006E46A2 /* CCTexture2D.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTexture2D.m; sourceTree = ""; }; + E02BB7C6126CC224006E46A2 /* CCTextureAtlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTextureAtlas.h; sourceTree = ""; }; + E02BB7C7126CC224006E46A2 /* CCTextureAtlas.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTextureAtlas.m; sourceTree = ""; }; + E02BB7C8126CC224006E46A2 /* CCTextureCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTextureCache.h; sourceTree = ""; }; + E02BB7C9126CC224006E46A2 /* CCTextureCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTextureCache.m; sourceTree = ""; }; + E02BB7CA126CC224006E46A2 /* CCTexturePVR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTexturePVR.h; sourceTree = ""; }; + E02BB7CB126CC224006E46A2 /* CCTexturePVR.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTexturePVR.m; sourceTree = ""; }; + E02BB7CC126CC224006E46A2 /* CCTileMapAtlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTileMapAtlas.h; sourceTree = ""; }; + E02BB7CD126CC224006E46A2 /* CCTileMapAtlas.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTileMapAtlas.m; sourceTree = ""; }; + E02BB7CE126CC224006E46A2 /* CCTMXLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTMXLayer.h; sourceTree = ""; }; + E02BB7CF126CC224006E46A2 /* CCTMXLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTMXLayer.m; sourceTree = ""; }; + E02BB7D0126CC224006E46A2 /* CCTMXObjectGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTMXObjectGroup.h; sourceTree = ""; }; + E02BB7D1126CC224006E46A2 /* CCTMXObjectGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTMXObjectGroup.m; sourceTree = ""; }; + E02BB7D2126CC224006E46A2 /* CCTMXTiledMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTMXTiledMap.h; sourceTree = ""; }; + E02BB7D3126CC224006E46A2 /* CCTMXTiledMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTMXTiledMap.m; sourceTree = ""; }; + E02BB7D4126CC224006E46A2 /* CCTMXXMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTMXXMLParser.h; sourceTree = ""; }; + E02BB7D5126CC224006E46A2 /* CCTMXXMLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTMXXMLParser.m; sourceTree = ""; }; + E02BB7D6126CC224006E46A2 /* CCTransition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTransition.h; sourceTree = ""; }; + E02BB7D7126CC224006E46A2 /* CCTransition.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTransition.m; sourceTree = ""; }; + E02BB7D8126CC224006E46A2 /* CCTransitionPageTurn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTransitionPageTurn.h; sourceTree = ""; }; + E02BB7D9126CC224006E46A2 /* CCTransitionPageTurn.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTransitionPageTurn.m; sourceTree = ""; }; + E02BB7DA126CC224006E46A2 /* CCTransitionRadial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTransitionRadial.h; sourceTree = ""; }; + E02BB7DB126CC224006E46A2 /* CCTransitionRadial.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTransitionRadial.m; sourceTree = ""; }; + E02BB7DC126CC224006E46A2 /* ccTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccTypes.h; sourceTree = ""; }; + E02BB7DD126CC224006E46A2 /* cocos2d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cocos2d.h; sourceTree = ""; }; + E02BB7DE126CC224006E46A2 /* cocos2d.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = cocos2d.m; sourceTree = ""; }; + E02BB7E0126CC224006E46A2 /* CCGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCGL.h; sourceTree = ""; }; + E02BB7E1126CC224006E46A2 /* CCNS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCNS.h; sourceTree = ""; }; + E02BB7E3126CC224006E46A2 /* CCDirectorIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCDirectorIOS.h; sourceTree = ""; }; + E02BB7E4126CC224006E46A2 /* CCDirectorIOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCDirectorIOS.m; sourceTree = ""; }; + E02BB7E5126CC224006E46A2 /* CCTouchDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTouchDelegateProtocol.h; sourceTree = ""; }; + E02BB7E6126CC224006E46A2 /* CCTouchDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTouchDispatcher.h; sourceTree = ""; }; + E02BB7E7126CC224006E46A2 /* CCTouchDispatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTouchDispatcher.m; sourceTree = ""; }; + E02BB7E8126CC224006E46A2 /* CCTouchHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTouchHandler.h; sourceTree = ""; }; + E02BB7E9126CC224006E46A2 /* CCTouchHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTouchHandler.m; sourceTree = ""; }; + E02BB7EA126CC224006E46A2 /* EAGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EAGLView.h; sourceTree = ""; }; + E02BB7EB126CC224006E46A2 /* EAGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EAGLView.m; sourceTree = ""; }; + E02BB7EC126CC224006E46A2 /* ES1Renderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ES1Renderer.h; sourceTree = ""; }; + E02BB7ED126CC224006E46A2 /* ES1Renderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ES1Renderer.m; sourceTree = ""; }; + E02BB7EE126CC224006E46A2 /* ESRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESRenderer.h; sourceTree = ""; }; + E02BB7EF126CC224006E46A2 /* glu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = glu.c; sourceTree = ""; }; + E02BB7F0126CC224006E46A2 /* glu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = glu.h; sourceTree = ""; }; + E02BB7F2126CC224006E46A2 /* CCDirectorMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCDirectorMac.h; sourceTree = ""; }; + E02BB7F3126CC224006E46A2 /* CCDirectorMac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCDirectorMac.m; sourceTree = ""; }; + E02BB7F4126CC224006E46A2 /* CCEventDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCEventDispatcher.h; sourceTree = ""; }; + E02BB7F5126CC224006E46A2 /* CCEventDispatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCEventDispatcher.m; sourceTree = ""; }; + E02BB7F6126CC224006E46A2 /* MacGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MacGLView.h; sourceTree = ""; }; + E02BB7F7126CC224006E46A2 /* MacGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MacGLView.m; sourceTree = ""; }; + E02BB7F9126CC224006E46A2 /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = base64.c; sourceTree = ""; }; + E02BB7FA126CC224006E46A2 /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base64.h; sourceTree = ""; }; + E02BB7FB126CC224006E46A2 /* CCArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCArray.h; sourceTree = ""; }; + E02BB7FC126CC224006E46A2 /* CCArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCArray.m; sourceTree = ""; }; + E02BB7FD126CC224006E46A2 /* ccCArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccCArray.h; sourceTree = ""; }; + E02BB7FE126CC224006E46A2 /* CCFileUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCFileUtils.h; sourceTree = ""; }; + E02BB7FF126CC224006E46A2 /* CCFileUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCFileUtils.m; sourceTree = ""; }; + E02BB800126CC224006E46A2 /* CCProfiling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCProfiling.h; sourceTree = ""; }; + E02BB801126CC224006E46A2 /* CCProfiling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCProfiling.m; sourceTree = ""; }; + E02BB802126CC224006E46A2 /* ccUtils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ccUtils.c; sourceTree = ""; }; + E02BB803126CC224006E46A2 /* ccUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccUtils.h; sourceTree = ""; }; + E02BB804126CC224006E46A2 /* CGPointExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CGPointExtension.h; sourceTree = ""; }; + E02BB805126CC224006E46A2 /* CGPointExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CGPointExtension.m; sourceTree = ""; }; + E02BB806126CC224006E46A2 /* OpenGL_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenGL_Internal.h; sourceTree = ""; }; + E02BB807126CC224006E46A2 /* TGAlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGAlib.h; sourceTree = ""; }; + E02BB808126CC224006E46A2 /* TGAlib.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGAlib.m; sourceTree = ""; }; + E02BB809126CC224006E46A2 /* TransformUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TransformUtils.h; sourceTree = ""; }; + E02BB80A126CC224006E46A2 /* TransformUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TransformUtils.m; sourceTree = ""; }; + E02BB80B126CC224006E46A2 /* uthash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uthash.h; sourceTree = ""; }; + E02BB80C126CC224006E46A2 /* utlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utlist.h; sourceTree = ""; }; + E02BB80D126CC224006E46A2 /* ZipUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZipUtils.h; sourceTree = ""; }; + E02BB80E126CC224006E46A2 /* ZipUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZipUtils.m; sourceTree = ""; }; + E0ECA3A8134E5E5600E7A048 /* CDataScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDataScanner.h; sourceTree = ""; }; + E0ECA3A9134E5E5600E7A048 /* CDataScanner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDataScanner.m; sourceTree = ""; }; + E0ECA3AB134E5E5600E7A048 /* CDataScanner_Extensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDataScanner_Extensions.h; sourceTree = ""; }; + E0ECA3AC134E5E5600E7A048 /* CDataScanner_Extensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDataScanner_Extensions.m; sourceTree = ""; }; + E0ECA3AD134E5E5600E7A048 /* NSDictionary_JSONExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSDictionary_JSONExtensions.h; sourceTree = ""; }; + E0ECA3AE134E5E5600E7A048 /* NSDictionary_JSONExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSDictionary_JSONExtensions.m; sourceTree = ""; }; + E0ECA3B0134E5E5600E7A048 /* CJSONDeserializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CJSONDeserializer.h; sourceTree = ""; }; + E0ECA3B1134E5E5600E7A048 /* CJSONDeserializer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CJSONDeserializer.m; sourceTree = ""; }; + E0ECA3B2134E5E5600E7A048 /* CJSONScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CJSONScanner.h; sourceTree = ""; }; + E0ECA3B3134E5E5600E7A048 /* CJSONScanner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CJSONScanner.m; sourceTree = ""; }; + E0ECA3B4134E5E5600E7A048 /* CJSONSerializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CJSONSerializer.h; sourceTree = ""; }; + E0ECA3B5134E5E5600E7A048 /* CJSONSerializer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CJSONSerializer.m; sourceTree = ""; }; + E0ECA3B6134E5E5600E7A048 /* JSONRepresentation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONRepresentation.h; sourceTree = ""; }; + E0F80F5D120A0182005866B8 /* GameConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GameConfig.h; sourceTree = ""; }; + E0F80F5E120A0182005866B8 /* RootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RootViewController.h; sourceTree = ""; }; + E0F80F5F120A0182005866B8 /* RootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RootViewController.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCCBF1B70F6022AE0040855A /* CoreGraphics.framework in Frameworks */, + DCCBF1B90F6022AE0040855A /* Foundation.framework in Frameworks */, + DCCBF1BB0F6022AE0040855A /* OpenGLES.framework in Frameworks */, + DCCBF1BD0F6022AE0040855A /* QuartzCore.framework in Frameworks */, + DCCBF1BF0F6022AE0040855A /* UIKit.framework in Frameworks */, + DC6640030F83B3EA000B3E49 /* AudioToolbox.framework in Frameworks */, + DC6640050F83B3EA000B3E49 /* OpenAL.framework in Frameworks */, + 506EDB88102F4C4000A389B3 /* libz.dylib in Frameworks */, + 506EDBA5102F4C9F00A389B3 /* AVFoundation.framework in Frameworks */, + 506EE1A91030508200A389B3 /* libcocos2d libraries.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 506EE05C10304ED200A389B3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 505574581045D68500A31725 /* AVFoundation.framework in Frameworks */, + 505574591045D68500A31725 /* AudioToolbox.framework in Frameworks */, + 5055745A1045D68500A31725 /* CoreGraphics.framework in Frameworks */, + 5055745B1045D68500A31725 /* OpenAL.framework in Frameworks */, + 5055745C1045D68500A31725 /* OpenGLES.framework in Frameworks */, + 5055745D1045D68500A31725 /* QuartzCore.framework in Frameworks */, + 5055745E1045D69D00A31725 /* libz.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 1D6058910D05DD3D006BFB54 /* CCMenuAdvanced.app */, + 506EE05E10304ED200A389B3 /* libcocos2d libraries.a */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + isa = PBXGroup; + children = ( + 504DFC6810AF1739006D82FE /* LICENSE.cocos2d */, + 504DFC6910AF1739006D82FE /* LICENSE.cocosdenshion */, + 506EDAA3102F461B00A389B3 /* cocos2d Sources */, + 2D500B1D0D5A766B00DBA0E3 /* Classes */, + 29B97315FDCFA39411CA2CEA /* Other Sources */, + 50F414EB1069373D002A0D5E /* Resources */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = CustomTemplate; + sourceTree = ""; + }; + 29B97315FDCFA39411CA2CEA /* Other Sources */ = { + isa = PBXGroup; + children = ( + 1F3B9A820EF2151B00286867 /* CCMenuAdvanced_Prefix.pch */, + 29B97316FDCFA39411CA2CEA /* main.m */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + DCCBF1B60F6022AE0040855A /* CoreGraphics.framework */, + DCCBF1B80F6022AE0040855A /* Foundation.framework */, + DCCBF1BA0F6022AE0040855A /* OpenGLES.framework */, + DCCBF1BC0F6022AE0040855A /* QuartzCore.framework */, + DCCBF1BE0F6022AE0040855A /* UIKit.framework */, + DC6640040F83B3EA000B3E49 /* OpenAL.framework */, + DC6640020F83B3EA000B3E49 /* AudioToolbox.framework */, + 506EDB87102F4C4000A389B3 /* libz.dylib */, + 506EDBA4102F4C9F00A389B3 /* AVFoundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 2D500B1D0D5A766B00DBA0E3 /* Classes */ = { + isa = PBXGroup; + children = ( + 6D78CB7613569C68007F0032 /* CCMenuAdvanced.h */, + 6D78CB7713569C68007F0032 /* CCMenuAdvanced.m */, + E0F80F5D120A0182005866B8 /* GameConfig.h */, + 506EDC2F102F528A00A389B3 /* HelloWorldLayer.h */, + 506EDC30102F528A00A389B3 /* HelloWorldLayer.m */, + E0F80F5E120A0182005866B8 /* RootViewController.h */, + E0F80F5F120A0182005866B8 /* RootViewController.m */, + 1F3B9A2C0EF2145700286867 /* CCMenuAdvancedAppDelegate.h */, + 1F3B9A2B0EF2145700286867 /* CCMenuAdvancedAppDelegate.m */, + ); + path = Classes; + sourceTree = ""; + }; + 504DFBC710AF12E9006D82FE /* cocoslive */ = { + isa = PBXGroup; + children = ( + 504DFBC810AF12E9006D82FE /* CLScoreServerPost.h */, + 504DFBC910AF12E9006D82FE /* CLScoreServerPost.m */, + 504DFBCA10AF12E9006D82FE /* CLScoreServerRequest.h */, + 504DFBCB10AF12E9006D82FE /* CLScoreServerRequest.m */, + 504DFBCC10AF12E9006D82FE /* cocoslive.h */, + 504DFBCD10AF12E9006D82FE /* cocoslive.m */, + ); + name = cocoslive; + path = libs/cocoslive; + sourceTree = ""; + }; + 506EDAA3102F461B00A389B3 /* cocos2d Sources */ = { + isa = PBXGroup; + children = ( + E02BB764126CC223006E46A2 /* cocos2d */, + 507ED63211C638C6002ED3FC /* CocosDenshion */, + 504DFBC710AF12E9006D82FE /* cocoslive */, + 50F41306106926B2002A0D5E /* FontLabel */, + E0ECA3A7134E5E5600E7A048 /* TouchJSON */, + ); + name = "cocos2d Sources"; + sourceTree = ""; + }; + 507ED63211C638C6002ED3FC /* CocosDenshion */ = { + isa = PBXGroup; + children = ( + 507ED63311C638C6002ED3FC /* CDAudioManager.h */, + 507ED63411C638C6002ED3FC /* CDAudioManager.m */, + 507ED63511C638C6002ED3FC /* CDConfig.h */, + 507ED63611C638C6002ED3FC /* CDOpenALSupport.h */, + 507ED63711C638C6002ED3FC /* CDOpenALSupport.m */, + 507ED63811C638C6002ED3FC /* CocosDenshion.h */, + 507ED63911C638C6002ED3FC /* CocosDenshion.m */, + 507ED63A11C638C6002ED3FC /* SimpleAudioEngine.h */, + 507ED63B11C638C6002ED3FC /* SimpleAudioEngine.m */, + ); + name = CocosDenshion; + path = libs/CocosDenshion; + sourceTree = ""; + }; + 50F41306106926B2002A0D5E /* FontLabel */ = { + isa = PBXGroup; + children = ( + 50674850107A3AF30090963A /* ZAttributedString.h */, + 50674851107A3AF30090963A /* ZAttributedString.m */, + 50674852107A3AF30090963A /* ZAttributedStringPrivate.h */, + 50F41307106926B2002A0D5E /* FontLabel.h */, + 50F41308106926B2002A0D5E /* FontLabel.m */, + 50F41309106926B2002A0D5E /* FontLabelStringDrawing.h */, + 50F4130A106926B2002A0D5E /* FontLabelStringDrawing.m */, + 50F4130B106926B2002A0D5E /* FontManager.h */, + 50F4130C106926B2002A0D5E /* FontManager.m */, + 50F4130F106926B2002A0D5E /* ZFont.h */, + 50F41310106926B2002A0D5E /* ZFont.m */, + ); + name = FontLabel; + path = libs/FontLabel; + sourceTree = ""; + }; + 50F414EB1069373D002A0D5E /* Resources */ = { + isa = PBXGroup; + children = ( + E02BB762126CC1E0006E46A2 /* iTunesArtwork */, + E02BB4FE126CA50F006E46A2 /* Icon@2x.png */, + E02BB4FF126CA50F006E46A2 /* Icon-Small@2x.png */, + E02BB500126CA50F006E46A2 /* Icon-Small.png */, + E02BB501126CA50F006E46A2 /* Icon-Small-50.png */, + E02BB502126CA50F006E46A2 /* Icon-72.png */, + 50F414EC1069373D002A0D5E /* Default.png */, + 50F414ED1069373D002A0D5E /* fps_images.png */, + 50F414EE1069373D002A0D5E /* Icon.png */, + 50F414EF1069373D002A0D5E /* Info.plist */, + ); + path = Resources; + sourceTree = ""; + }; + E02BB764126CC223006E46A2 /* cocos2d */ = { + isa = PBXGroup; + children = ( + E02BB765126CC223006E46A2 /* CCAction.h */, + E02BB766126CC223006E46A2 /* CCAction.m */, + E02BB767126CC223006E46A2 /* CCActionCamera.h */, + E02BB768126CC223006E46A2 /* CCActionCamera.m */, + E02BB769126CC223006E46A2 /* CCActionEase.h */, + E02BB76A126CC223006E46A2 /* CCActionEase.m */, + E02BB76B126CC223006E46A2 /* CCActionGrid.h */, + E02BB76C126CC223006E46A2 /* CCActionGrid.m */, + E02BB76D126CC223006E46A2 /* CCActionGrid3D.h */, + E02BB76E126CC223006E46A2 /* CCActionGrid3D.m */, + E02BB76F126CC223006E46A2 /* CCActionInstant.h */, + E02BB770126CC223006E46A2 /* CCActionInstant.m */, + E02BB771126CC223006E46A2 /* CCActionInterval.h */, + E02BB772126CC223006E46A2 /* CCActionInterval.m */, + E02BB773126CC223006E46A2 /* CCActionManager.h */, + E02BB774126CC223006E46A2 /* CCActionManager.m */, + E02BB775126CC223006E46A2 /* CCActionPageTurn3D.h */, + E02BB776126CC223006E46A2 /* CCActionPageTurn3D.m */, + E02BB777126CC223006E46A2 /* CCActionProgressTimer.h */, + E02BB778126CC223006E46A2 /* CCActionProgressTimer.m */, + E02BB779126CC223006E46A2 /* CCActionTiledGrid.h */, + E02BB77A126CC223006E46A2 /* CCActionTiledGrid.m */, + E02BB77B126CC223006E46A2 /* CCActionTween.h */, + E02BB77C126CC223006E46A2 /* CCActionTween.m */, + E02BB77D126CC223006E46A2 /* CCAnimation.h */, + E02BB77E126CC223006E46A2 /* CCAnimation.m */, + E02BB77F126CC223006E46A2 /* CCAnimationCache.h */, + E02BB780126CC223006E46A2 /* CCAnimationCache.m */, + E02BB781126CC223006E46A2 /* CCAtlasNode.h */, + E02BB782126CC223006E46A2 /* CCAtlasNode.m */, + E02BB783126CC223006E46A2 /* CCBlockSupport.h */, + E02BB784126CC223006E46A2 /* CCBlockSupport.m */, + E02BB785126CC223006E46A2 /* CCCamera.h */, + E02BB786126CC223006E46A2 /* CCCamera.m */, + E02BB789126CC223006E46A2 /* ccConfig.h */, + E02BB78A126CC223006E46A2 /* CCConfiguration.h */, + E02BB78B126CC223006E46A2 /* CCConfiguration.m */, + E02BB78C126CC223006E46A2 /* CCDirector.h */, + E02BB78D126CC223006E46A2 /* CCDirector.m */, + E02BB78E126CC223006E46A2 /* CCDrawingPrimitives.h */, + E02BB78F126CC223006E46A2 /* CCDrawingPrimitives.m */, + E02BB790126CC223006E46A2 /* CCGrabber.h */, + E02BB791126CC223006E46A2 /* CCGrabber.m */, + E02BB792126CC223006E46A2 /* CCGrid.h */, + E02BB793126CC223006E46A2 /* CCGrid.m */, + E02BB794126CC223006E46A2 /* CCLabelAtlas.h */, + E02BB795126CC223006E46A2 /* CCLabelAtlas.m */, + E02BB796126CC223006E46A2 /* CCLabelBMFont.h */, + E02BB797126CC223006E46A2 /* CCLabelBMFont.m */, + E02BB798126CC223006E46A2 /* CCLabelTTF.h */, + E02BB799126CC223006E46A2 /* CCLabelTTF.m */, + E02BB79A126CC223006E46A2 /* CCLayer.h */, + E02BB79B126CC223006E46A2 /* CCLayer.m */, + E02BB79C126CC223006E46A2 /* ccMacros.h */, + E02BB79D126CC223006E46A2 /* CCMenu.h */, + E02BB79E126CC223006E46A2 /* CCMenu.m */, + E02BB79F126CC224006E46A2 /* CCMenuItem.h */, + E02BB7A0126CC224006E46A2 /* CCMenuItem.m */, + E02BB7A1126CC224006E46A2 /* CCMotionStreak.h */, + E02BB7A2126CC224006E46A2 /* CCMotionStreak.m */, + E02BB7A3126CC224006E46A2 /* CCNode.h */, + E02BB7A4126CC224006E46A2 /* CCNode.m */, + E02BB7A5126CC224006E46A2 /* CCParallaxNode.h */, + E02BB7A6126CC224006E46A2 /* CCParallaxNode.m */, + E02BB7A7126CC224006E46A2 /* CCParticleExamples.h */, + E02BB7A8126CC224006E46A2 /* CCParticleExamples.m */, + E02BB7A9126CC224006E46A2 /* CCParticleSystem.h */, + E02BB7AA126CC224006E46A2 /* CCParticleSystem.m */, + E02BB7AB126CC224006E46A2 /* CCParticleSystemPoint.h */, + E02BB7AC126CC224006E46A2 /* CCParticleSystemPoint.m */, + E02BB7AD126CC224006E46A2 /* CCParticleSystemQuad.h */, + E02BB7AE126CC224006E46A2 /* CCParticleSystemQuad.m */, + E02BB7AF126CC224006E46A2 /* CCProgressTimer.h */, + E02BB7B0126CC224006E46A2 /* CCProgressTimer.m */, + E02BB7B1126CC224006E46A2 /* CCProtocols.h */, + E02BB7B2126CC224006E46A2 /* CCRenderTexture.h */, + E02BB7B3126CC224006E46A2 /* CCRenderTexture.m */, + E02BB7B4126CC224006E46A2 /* CCRibbon.h */, + E02BB7B5126CC224006E46A2 /* CCRibbon.m */, + E02BB7B6126CC224006E46A2 /* CCScene.h */, + E02BB7B7126CC224006E46A2 /* CCScene.m */, + E02BB7B8126CC224006E46A2 /* CCScheduler.h */, + E02BB7B9126CC224006E46A2 /* CCScheduler.m */, + E02BB7BA126CC224006E46A2 /* CCSprite.h */, + E02BB7BB126CC224006E46A2 /* CCSprite.m */, + E02BB7BC126CC224006E46A2 /* CCSpriteBatchNode.h */, + E02BB7BD126CC224006E46A2 /* CCSpriteBatchNode.m */, + E02BB7BE126CC224006E46A2 /* CCSpriteFrame.h */, + E02BB7BF126CC224006E46A2 /* CCSpriteFrame.m */, + E02BB7C0126CC224006E46A2 /* CCSpriteFrameCache.h */, + E02BB7C1126CC224006E46A2 /* CCSpriteFrameCache.m */, + E02BB7C4126CC224006E46A2 /* CCTexture2D.h */, + E02BB7C5126CC224006E46A2 /* CCTexture2D.m */, + E02BB7C6126CC224006E46A2 /* CCTextureAtlas.h */, + E02BB7C7126CC224006E46A2 /* CCTextureAtlas.m */, + E02BB7C8126CC224006E46A2 /* CCTextureCache.h */, + E02BB7C9126CC224006E46A2 /* CCTextureCache.m */, + E02BB7CA126CC224006E46A2 /* CCTexturePVR.h */, + E02BB7CB126CC224006E46A2 /* CCTexturePVR.m */, + E02BB7CC126CC224006E46A2 /* CCTileMapAtlas.h */, + E02BB7CD126CC224006E46A2 /* CCTileMapAtlas.m */, + E02BB7CE126CC224006E46A2 /* CCTMXLayer.h */, + E02BB7CF126CC224006E46A2 /* CCTMXLayer.m */, + E02BB7D0126CC224006E46A2 /* CCTMXObjectGroup.h */, + E02BB7D1126CC224006E46A2 /* CCTMXObjectGroup.m */, + E02BB7D2126CC224006E46A2 /* CCTMXTiledMap.h */, + E02BB7D3126CC224006E46A2 /* CCTMXTiledMap.m */, + E02BB7D4126CC224006E46A2 /* CCTMXXMLParser.h */, + E02BB7D5126CC224006E46A2 /* CCTMXXMLParser.m */, + E02BB7D6126CC224006E46A2 /* CCTransition.h */, + E02BB7D7126CC224006E46A2 /* CCTransition.m */, + E02BB7D8126CC224006E46A2 /* CCTransitionPageTurn.h */, + E02BB7D9126CC224006E46A2 /* CCTransitionPageTurn.m */, + E02BB7DA126CC224006E46A2 /* CCTransitionRadial.h */, + E02BB7DB126CC224006E46A2 /* CCTransitionRadial.m */, + E02BB7DC126CC224006E46A2 /* ccTypes.h */, + E02BB7DD126CC224006E46A2 /* cocos2d.h */, + E02BB7DE126CC224006E46A2 /* cocos2d.m */, + E02BB7DF126CC224006E46A2 /* Platforms */, + E02BB7F8126CC224006E46A2 /* Support */, + ); + name = cocos2d; + path = libs/cocos2d; + sourceTree = ""; + }; + E02BB7DF126CC224006E46A2 /* Platforms */ = { + isa = PBXGroup; + children = ( + E02BB7E0126CC224006E46A2 /* CCGL.h */, + E02BB7E1126CC224006E46A2 /* CCNS.h */, + E02BB7E2126CC224006E46A2 /* iOS */, + E02BB7F1126CC224006E46A2 /* Mac */, + ); + path = Platforms; + sourceTree = ""; + }; + E02BB7E2126CC224006E46A2 /* iOS */ = { + isa = PBXGroup; + children = ( + E02BB7E3126CC224006E46A2 /* CCDirectorIOS.h */, + E02BB7E4126CC224006E46A2 /* CCDirectorIOS.m */, + E02BB7E5126CC224006E46A2 /* CCTouchDelegateProtocol.h */, + E02BB7E6126CC224006E46A2 /* CCTouchDispatcher.h */, + E02BB7E7126CC224006E46A2 /* CCTouchDispatcher.m */, + E02BB7E8126CC224006E46A2 /* CCTouchHandler.h */, + E02BB7E9126CC224006E46A2 /* CCTouchHandler.m */, + E02BB7EA126CC224006E46A2 /* EAGLView.h */, + E02BB7EB126CC224006E46A2 /* EAGLView.m */, + E02BB7EC126CC224006E46A2 /* ES1Renderer.h */, + E02BB7ED126CC224006E46A2 /* ES1Renderer.m */, + E02BB7EE126CC224006E46A2 /* ESRenderer.h */, + E02BB7EF126CC224006E46A2 /* glu.c */, + E02BB7F0126CC224006E46A2 /* glu.h */, + ); + path = iOS; + sourceTree = ""; + }; + E02BB7F1126CC224006E46A2 /* Mac */ = { + isa = PBXGroup; + children = ( + E02BB7F2126CC224006E46A2 /* CCDirectorMac.h */, + E02BB7F3126CC224006E46A2 /* CCDirectorMac.m */, + E02BB7F4126CC224006E46A2 /* CCEventDispatcher.h */, + E02BB7F5126CC224006E46A2 /* CCEventDispatcher.m */, + E02BB7F6126CC224006E46A2 /* MacGLView.h */, + E02BB7F7126CC224006E46A2 /* MacGLView.m */, + ); + path = Mac; + sourceTree = ""; + }; + E02BB7F8126CC224006E46A2 /* Support */ = { + isa = PBXGroup; + children = ( + E02BB7F9126CC224006E46A2 /* base64.c */, + E02BB7FA126CC224006E46A2 /* base64.h */, + E02BB7FB126CC224006E46A2 /* CCArray.h */, + E02BB7FC126CC224006E46A2 /* CCArray.m */, + E02BB7FD126CC224006E46A2 /* ccCArray.h */, + E02BB7FE126CC224006E46A2 /* CCFileUtils.h */, + E02BB7FF126CC224006E46A2 /* CCFileUtils.m */, + E02BB800126CC224006E46A2 /* CCProfiling.h */, + E02BB801126CC224006E46A2 /* CCProfiling.m */, + E02BB802126CC224006E46A2 /* ccUtils.c */, + E02BB803126CC224006E46A2 /* ccUtils.h */, + E02BB804126CC224006E46A2 /* CGPointExtension.h */, + E02BB805126CC224006E46A2 /* CGPointExtension.m */, + E02BB806126CC224006E46A2 /* OpenGL_Internal.h */, + E02BB807126CC224006E46A2 /* TGAlib.h */, + E02BB808126CC224006E46A2 /* TGAlib.m */, + E02BB809126CC224006E46A2 /* TransformUtils.h */, + E02BB80A126CC224006E46A2 /* TransformUtils.m */, + E02BB80B126CC224006E46A2 /* uthash.h */, + E02BB80C126CC224006E46A2 /* utlist.h */, + E02BB80D126CC224006E46A2 /* ZipUtils.h */, + E02BB80E126CC224006E46A2 /* ZipUtils.m */, + ); + path = Support; + sourceTree = ""; + }; + E0ECA3A7134E5E5600E7A048 /* TouchJSON */ = { + isa = PBXGroup; + children = ( + E0ECA3A8134E5E5600E7A048 /* CDataScanner.h */, + E0ECA3A9134E5E5600E7A048 /* CDataScanner.m */, + E0ECA3AA134E5E5600E7A048 /* Extensions */, + E0ECA3AF134E5E5600E7A048 /* JSON */, + ); + name = TouchJSON; + path = libs/TouchJSON; + sourceTree = ""; + }; + E0ECA3AA134E5E5600E7A048 /* Extensions */ = { + isa = PBXGroup; + children = ( + E0ECA3AB134E5E5600E7A048 /* CDataScanner_Extensions.h */, + E0ECA3AC134E5E5600E7A048 /* CDataScanner_Extensions.m */, + E0ECA3AD134E5E5600E7A048 /* NSDictionary_JSONExtensions.h */, + E0ECA3AE134E5E5600E7A048 /* NSDictionary_JSONExtensions.m */, + ); + path = Extensions; + sourceTree = ""; + }; + E0ECA3AF134E5E5600E7A048 /* JSON */ = { + isa = PBXGroup; + children = ( + E0ECA3B0134E5E5600E7A048 /* CJSONDeserializer.h */, + E0ECA3B1134E5E5600E7A048 /* CJSONDeserializer.m */, + E0ECA3B2134E5E5600E7A048 /* CJSONScanner.h */, + E0ECA3B3134E5E5600E7A048 /* CJSONScanner.m */, + E0ECA3B4134E5E5600E7A048 /* CJSONSerializer.h */, + E0ECA3B5134E5E5600E7A048 /* CJSONSerializer.m */, + E0ECA3B6134E5E5600E7A048 /* JSONRepresentation.h */, + ); + path = JSON; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 506EE05A10304ED200A389B3 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 50F4132A106926B2002A0D5E /* FontLabel.h in Headers */, + 50F4132C106926B2002A0D5E /* FontLabelStringDrawing.h in Headers */, + 50F4132E106926B2002A0D5E /* FontManager.h in Headers */, + 50F41332106926B2002A0D5E /* ZFont.h in Headers */, + 50674853107A3AF30090963A /* ZAttributedString.h in Headers */, + 50674855107A3AF30090963A /* ZAttributedStringPrivate.h in Headers */, + 504DFBCE10AF12E9006D82FE /* CLScoreServerPost.h in Headers */, + 504DFBD010AF12E9006D82FE /* CLScoreServerRequest.h in Headers */, + 504DFBD210AF12E9006D82FE /* cocoslive.h in Headers */, + 507ED63C11C638C6002ED3FC /* CDAudioManager.h in Headers */, + 507ED63E11C638C6002ED3FC /* CDConfig.h in Headers */, + 507ED63F11C638C6002ED3FC /* CDOpenALSupport.h in Headers */, + 507ED64111C638C6002ED3FC /* CocosDenshion.h in Headers */, + 507ED64311C638C6002ED3FC /* SimpleAudioEngine.h in Headers */, + E02BB80F126CC224006E46A2 /* CCAction.h in Headers */, + E02BB811126CC224006E46A2 /* CCActionCamera.h in Headers */, + E02BB813126CC224006E46A2 /* CCActionEase.h in Headers */, + E02BB815126CC224006E46A2 /* CCActionGrid.h in Headers */, + E02BB817126CC224006E46A2 /* CCActionGrid3D.h in Headers */, + E02BB819126CC224006E46A2 /* CCActionInstant.h in Headers */, + E02BB81B126CC224006E46A2 /* CCActionInterval.h in Headers */, + E02BB81D126CC224006E46A2 /* CCActionManager.h in Headers */, + E02BB81F126CC224006E46A2 /* CCActionPageTurn3D.h in Headers */, + E02BB821126CC224006E46A2 /* CCActionProgressTimer.h in Headers */, + E02BB823126CC224006E46A2 /* CCActionTiledGrid.h in Headers */, + E02BB825126CC224006E46A2 /* CCActionTween.h in Headers */, + E02BB827126CC224006E46A2 /* CCAnimation.h in Headers */, + E02BB829126CC224006E46A2 /* CCAnimationCache.h in Headers */, + E02BB82B126CC224006E46A2 /* CCAtlasNode.h in Headers */, + E02BB82D126CC224006E46A2 /* CCBlockSupport.h in Headers */, + E02BB82F126CC224006E46A2 /* CCCamera.h in Headers */, + E02BB833126CC224006E46A2 /* ccConfig.h in Headers */, + E02BB834126CC224006E46A2 /* CCConfiguration.h in Headers */, + E02BB836126CC224006E46A2 /* CCDirector.h in Headers */, + E02BB838126CC224006E46A2 /* CCDrawingPrimitives.h in Headers */, + E02BB83A126CC224006E46A2 /* CCGrabber.h in Headers */, + E02BB83C126CC224006E46A2 /* CCGrid.h in Headers */, + E02BB83E126CC224006E46A2 /* CCLabelAtlas.h in Headers */, + E02BB840126CC224006E46A2 /* CCLabelBMFont.h in Headers */, + E02BB842126CC224006E46A2 /* CCLabelTTF.h in Headers */, + E02BB844126CC224006E46A2 /* CCLayer.h in Headers */, + E02BB846126CC224006E46A2 /* ccMacros.h in Headers */, + E02BB847126CC224006E46A2 /* CCMenu.h in Headers */, + E02BB849126CC224006E46A2 /* CCMenuItem.h in Headers */, + E02BB84B126CC224006E46A2 /* CCMotionStreak.h in Headers */, + E02BB84D126CC224006E46A2 /* CCNode.h in Headers */, + E02BB84F126CC224006E46A2 /* CCParallaxNode.h in Headers */, + E02BB851126CC224006E46A2 /* CCParticleExamples.h in Headers */, + E02BB853126CC224006E46A2 /* CCParticleSystem.h in Headers */, + E02BB855126CC224006E46A2 /* CCParticleSystemPoint.h in Headers */, + E02BB857126CC224006E46A2 /* CCParticleSystemQuad.h in Headers */, + E02BB859126CC224006E46A2 /* CCProgressTimer.h in Headers */, + E02BB85B126CC224006E46A2 /* CCProtocols.h in Headers */, + E02BB85C126CC224006E46A2 /* CCRenderTexture.h in Headers */, + E02BB85E126CC224006E46A2 /* CCRibbon.h in Headers */, + E02BB860126CC224006E46A2 /* CCScene.h in Headers */, + E02BB862126CC224006E46A2 /* CCScheduler.h in Headers */, + E02BB864126CC224006E46A2 /* CCSprite.h in Headers */, + E02BB866126CC224006E46A2 /* CCSpriteBatchNode.h in Headers */, + E02BB868126CC224006E46A2 /* CCSpriteFrame.h in Headers */, + E02BB86A126CC224006E46A2 /* CCSpriteFrameCache.h in Headers */, + E02BB86E126CC224006E46A2 /* CCTexture2D.h in Headers */, + E02BB870126CC224006E46A2 /* CCTextureAtlas.h in Headers */, + E02BB872126CC224006E46A2 /* CCTextureCache.h in Headers */, + E02BB874126CC224006E46A2 /* CCTexturePVR.h in Headers */, + E02BB876126CC224006E46A2 /* CCTileMapAtlas.h in Headers */, + E02BB878126CC224006E46A2 /* CCTMXLayer.h in Headers */, + E02BB87A126CC224006E46A2 /* CCTMXObjectGroup.h in Headers */, + E02BB87C126CC224006E46A2 /* CCTMXTiledMap.h in Headers */, + E02BB87E126CC224006E46A2 /* CCTMXXMLParser.h in Headers */, + E02BB880126CC224006E46A2 /* CCTransition.h in Headers */, + E02BB882126CC224006E46A2 /* CCTransitionPageTurn.h in Headers */, + E02BB884126CC224006E46A2 /* CCTransitionRadial.h in Headers */, + E02BB886126CC224006E46A2 /* ccTypes.h in Headers */, + E02BB887126CC224006E46A2 /* cocos2d.h in Headers */, + E02BB889126CC224006E46A2 /* CCGL.h in Headers */, + E02BB88A126CC224006E46A2 /* CCNS.h in Headers */, + E02BB88B126CC224006E46A2 /* CCDirectorIOS.h in Headers */, + E02BB88D126CC224006E46A2 /* CCTouchDelegateProtocol.h in Headers */, + E02BB88E126CC224006E46A2 /* CCTouchDispatcher.h in Headers */, + E02BB890126CC224006E46A2 /* CCTouchHandler.h in Headers */, + E02BB892126CC224006E46A2 /* EAGLView.h in Headers */, + E02BB894126CC224006E46A2 /* ES1Renderer.h in Headers */, + E02BB896126CC224006E46A2 /* ESRenderer.h in Headers */, + E02BB898126CC224006E46A2 /* glu.h in Headers */, + E02BB899126CC224006E46A2 /* CCDirectorMac.h in Headers */, + E02BB89B126CC224006E46A2 /* CCEventDispatcher.h in Headers */, + E02BB89D126CC224006E46A2 /* MacGLView.h in Headers */, + E02BB8A0126CC224006E46A2 /* base64.h in Headers */, + E02BB8A1126CC224006E46A2 /* CCArray.h in Headers */, + E02BB8A3126CC224006E46A2 /* ccCArray.h in Headers */, + E02BB8A4126CC224006E46A2 /* CCFileUtils.h in Headers */, + E02BB8A6126CC224006E46A2 /* CCProfiling.h in Headers */, + E02BB8A9126CC224006E46A2 /* ccUtils.h in Headers */, + E02BB8AA126CC224006E46A2 /* CGPointExtension.h in Headers */, + E02BB8AC126CC224006E46A2 /* OpenGL_Internal.h in Headers */, + E02BB8AD126CC224006E46A2 /* TGAlib.h in Headers */, + E02BB8AF126CC224006E46A2 /* TransformUtils.h in Headers */, + E02BB8B1126CC224006E46A2 /* uthash.h in Headers */, + E02BB8B2126CC224006E46A2 /* utlist.h in Headers */, + E02BB8B3126CC224006E46A2 /* ZipUtils.h in Headers */, + E0ECA3B7134E5E5600E7A048 /* CDataScanner.h in Headers */, + E0ECA3B9134E5E5600E7A048 /* CDataScanner_Extensions.h in Headers */, + E0ECA3BB134E5E5600E7A048 /* NSDictionary_JSONExtensions.h in Headers */, + E0ECA3BD134E5E5600E7A048 /* CJSONDeserializer.h in Headers */, + E0ECA3BF134E5E5600E7A048 /* CJSONScanner.h in Headers */, + E0ECA3C1134E5E5600E7A048 /* CJSONSerializer.h in Headers */, + E0ECA3C3134E5E5600E7A048 /* JSONRepresentation.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 1D6058900D05DD3D006BFB54 /* CCMenuAdvanced */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "CCMenuAdvanced" */; + buildPhases = ( + 1D60588D0D05DD3D006BFB54 /* Resources */, + 1D60588E0D05DD3D006BFB54 /* Sources */, + 1D60588F0D05DD3D006BFB54 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 506EE1A81030507B00A389B3 /* PBXTargetDependency */, + ); + name = CCMenuAdvanced; + productName = CCMenuAdvanced; + productReference = 1D6058910D05DD3D006BFB54 /* CCMenuAdvanced.app */; + productType = "com.apple.product-type.application"; + }; + 506EE05D10304ED200A389B3 /* cocos2d libraries */ = { + isa = PBXNativeTarget; + buildConfigurationList = 506EE06410304F0100A389B3 /* Build configuration list for PBXNativeTarget "cocos2d libraries" */; + buildPhases = ( + 506EE05A10304ED200A389B3 /* Headers */, + 506EE05B10304ED200A389B3 /* Sources */, + 506EE05C10304ED200A389B3 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "cocos2d libraries"; + productName = "cocos2d libraries"; + productReference = 506EE05E10304ED200A389B3 /* libcocos2d libraries.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "CCMenuAdvanced" */; + compatibilityVersion = "Xcode 3.1"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D6058900D05DD3D006BFB54 /* CCMenuAdvanced */, + 506EE05D10304ED200A389B3 /* cocos2d libraries */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1D60588D0D05DD3D006BFB54 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 50F414F01069373D002A0D5E /* Default.png in Resources */, + 50F414F11069373D002A0D5E /* fps_images.png in Resources */, + 50F414F21069373D002A0D5E /* Icon.png in Resources */, + E02BB504126CA50F006E46A2 /* Icon@2x.png in Resources */, + E02BB505126CA50F006E46A2 /* Icon-Small@2x.png in Resources */, + E02BB506126CA50F006E46A2 /* Icon-Small.png in Resources */, + E02BB507126CA50F006E46A2 /* Icon-Small-50.png in Resources */, + E02BB508126CA50F006E46A2 /* Icon-72.png in Resources */, + E02BB763126CC1E0006E46A2 /* iTunesArtwork in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D60588E0D05DD3D006BFB54 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D60589B0D05DD56006BFB54 /* main.m in Sources */, + 1F3B9A2D0EF2145700286867 /* CCMenuAdvancedAppDelegate.m in Sources */, + 506EDC31102F528A00A389B3 /* HelloWorldLayer.m in Sources */, + E0F80F60120A0182005866B8 /* RootViewController.m in Sources */, + 6D78CB7813569C68007F0032 /* CCMenuAdvanced.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 506EE05B10304ED200A389B3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 50F4132B106926B2002A0D5E /* FontLabel.m in Sources */, + 50F4132D106926B2002A0D5E /* FontLabelStringDrawing.m in Sources */, + 50F4132F106926B2002A0D5E /* FontManager.m in Sources */, + 50F41333106926B2002A0D5E /* ZFont.m in Sources */, + 50674854107A3AF30090963A /* ZAttributedString.m in Sources */, + 504DFBCF10AF12E9006D82FE /* CLScoreServerPost.m in Sources */, + 504DFBD110AF12E9006D82FE /* CLScoreServerRequest.m in Sources */, + 504DFBD310AF12E9006D82FE /* cocoslive.m in Sources */, + 507ED63D11C638C6002ED3FC /* CDAudioManager.m in Sources */, + 507ED64011C638C6002ED3FC /* CDOpenALSupport.m in Sources */, + 507ED64211C638C6002ED3FC /* CocosDenshion.m in Sources */, + 507ED64411C638C6002ED3FC /* SimpleAudioEngine.m in Sources */, + E02BB810126CC224006E46A2 /* CCAction.m in Sources */, + E02BB812126CC224006E46A2 /* CCActionCamera.m in Sources */, + E02BB814126CC224006E46A2 /* CCActionEase.m in Sources */, + E02BB816126CC224006E46A2 /* CCActionGrid.m in Sources */, + E02BB818126CC224006E46A2 /* CCActionGrid3D.m in Sources */, + E02BB81A126CC224006E46A2 /* CCActionInstant.m in Sources */, + E02BB81C126CC224006E46A2 /* CCActionInterval.m in Sources */, + E02BB81E126CC224006E46A2 /* CCActionManager.m in Sources */, + E02BB820126CC224006E46A2 /* CCActionPageTurn3D.m in Sources */, + E02BB822126CC224006E46A2 /* CCActionProgressTimer.m in Sources */, + E02BB824126CC224006E46A2 /* CCActionTiledGrid.m in Sources */, + E02BB826126CC224006E46A2 /* CCActionTween.m in Sources */, + E02BB828126CC224006E46A2 /* CCAnimation.m in Sources */, + E02BB82A126CC224006E46A2 /* CCAnimationCache.m in Sources */, + E02BB82C126CC224006E46A2 /* CCAtlasNode.m in Sources */, + E02BB82E126CC224006E46A2 /* CCBlockSupport.m in Sources */, + E02BB830126CC224006E46A2 /* CCCamera.m in Sources */, + E02BB835126CC224006E46A2 /* CCConfiguration.m in Sources */, + E02BB837126CC224006E46A2 /* CCDirector.m in Sources */, + E02BB839126CC224006E46A2 /* CCDrawingPrimitives.m in Sources */, + E02BB83B126CC224006E46A2 /* CCGrabber.m in Sources */, + E02BB83D126CC224006E46A2 /* CCGrid.m in Sources */, + E02BB83F126CC224006E46A2 /* CCLabelAtlas.m in Sources */, + E02BB841126CC224006E46A2 /* CCLabelBMFont.m in Sources */, + E02BB843126CC224006E46A2 /* CCLabelTTF.m in Sources */, + E02BB845126CC224006E46A2 /* CCLayer.m in Sources */, + E02BB848126CC224006E46A2 /* CCMenu.m in Sources */, + E02BB84A126CC224006E46A2 /* CCMenuItem.m in Sources */, + E02BB84C126CC224006E46A2 /* CCMotionStreak.m in Sources */, + E02BB84E126CC224006E46A2 /* CCNode.m in Sources */, + E02BB850126CC224006E46A2 /* CCParallaxNode.m in Sources */, + E02BB852126CC224006E46A2 /* CCParticleExamples.m in Sources */, + E02BB854126CC224006E46A2 /* CCParticleSystem.m in Sources */, + E02BB856126CC224006E46A2 /* CCParticleSystemPoint.m in Sources */, + E02BB858126CC224006E46A2 /* CCParticleSystemQuad.m in Sources */, + E02BB85A126CC224006E46A2 /* CCProgressTimer.m in Sources */, + E02BB85D126CC224006E46A2 /* CCRenderTexture.m in Sources */, + E02BB85F126CC224006E46A2 /* CCRibbon.m in Sources */, + E02BB861126CC224006E46A2 /* CCScene.m in Sources */, + E02BB863126CC224006E46A2 /* CCScheduler.m in Sources */, + E02BB865126CC224006E46A2 /* CCSprite.m in Sources */, + E02BB867126CC224006E46A2 /* CCSpriteBatchNode.m in Sources */, + E02BB869126CC224006E46A2 /* CCSpriteFrame.m in Sources */, + E02BB86B126CC224006E46A2 /* CCSpriteFrameCache.m in Sources */, + E02BB86F126CC224006E46A2 /* CCTexture2D.m in Sources */, + E02BB871126CC224006E46A2 /* CCTextureAtlas.m in Sources */, + E02BB873126CC224006E46A2 /* CCTextureCache.m in Sources */, + E02BB875126CC224006E46A2 /* CCTexturePVR.m in Sources */, + E02BB877126CC224006E46A2 /* CCTileMapAtlas.m in Sources */, + E02BB879126CC224006E46A2 /* CCTMXLayer.m in Sources */, + E02BB87B126CC224006E46A2 /* CCTMXObjectGroup.m in Sources */, + E02BB87D126CC224006E46A2 /* CCTMXTiledMap.m in Sources */, + E02BB87F126CC224006E46A2 /* CCTMXXMLParser.m in Sources */, + E02BB881126CC224006E46A2 /* CCTransition.m in Sources */, + E02BB883126CC224006E46A2 /* CCTransitionPageTurn.m in Sources */, + E02BB885126CC224006E46A2 /* CCTransitionRadial.m in Sources */, + E02BB888126CC224006E46A2 /* cocos2d.m in Sources */, + E02BB88C126CC224006E46A2 /* CCDirectorIOS.m in Sources */, + E02BB88F126CC224006E46A2 /* CCTouchDispatcher.m in Sources */, + E02BB891126CC224006E46A2 /* CCTouchHandler.m in Sources */, + E02BB893126CC224006E46A2 /* EAGLView.m in Sources */, + E02BB895126CC224006E46A2 /* ES1Renderer.m in Sources */, + E02BB897126CC224006E46A2 /* glu.c in Sources */, + E02BB89A126CC224006E46A2 /* CCDirectorMac.m in Sources */, + E02BB89C126CC224006E46A2 /* CCEventDispatcher.m in Sources */, + E02BB89E126CC224006E46A2 /* MacGLView.m in Sources */, + E02BB89F126CC224006E46A2 /* base64.c in Sources */, + E02BB8A2126CC224006E46A2 /* CCArray.m in Sources */, + E02BB8A5126CC224006E46A2 /* CCFileUtils.m in Sources */, + E02BB8A7126CC224006E46A2 /* CCProfiling.m in Sources */, + E02BB8A8126CC224006E46A2 /* ccUtils.c in Sources */, + E02BB8AB126CC224006E46A2 /* CGPointExtension.m in Sources */, + E02BB8AE126CC224006E46A2 /* TGAlib.m in Sources */, + E02BB8B0126CC224006E46A2 /* TransformUtils.m in Sources */, + E02BB8B4126CC224006E46A2 /* ZipUtils.m in Sources */, + E0ECA3B8134E5E5600E7A048 /* CDataScanner.m in Sources */, + E0ECA3BA134E5E5600E7A048 /* CDataScanner_Extensions.m in Sources */, + E0ECA3BC134E5E5600E7A048 /* NSDictionary_JSONExtensions.m in Sources */, + E0ECA3BE134E5E5600E7A048 /* CJSONDeserializer.m in Sources */, + E0ECA3C0134E5E5600E7A048 /* CJSONScanner.m in Sources */, + E0ECA3C2134E5E5600E7A048 /* CJSONSerializer.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 506EE1A81030507B00A389B3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 506EE05D10304ED200A389B3 /* cocos2d libraries */; + targetProxy = 506EE1A71030507B00A389B3 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 1D6058940D05DD3E006BFB54 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = CCMenuAdvanced_Prefix.pch; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + INFOPLIST_FILE = Resources/Info.plist; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = ( + "-all_load", + "-ObjC", + ); + PREBINDING = NO; + PRODUCT_NAME = CCMenuAdvanced; + WARNING_CFLAGS = "-Wall"; + }; + name = Debug; + }; + 1D6058950D05DD3E006BFB54 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = CCMenuAdvanced_Prefix.pch; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + INFOPLIST_FILE = Resources/Info.plist; + ONLY_ACTIVE_ARCH = NO; + OTHER_LDFLAGS = ( + "-all_load", + "-ObjC", + ); + PREBINDING = NO; + PRODUCT_NAME = CCMenuAdvanced; + WARNING_CFLAGS = "-Wall"; + }; + name = Release; + }; + 506EE05F10304ED500A389B3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + PREBINDING = NO; + PRODUCT_NAME = "cocos2d libraries"; + }; + name = Debug; + }; + 506EE06010304ED500A389B3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + PREBINDING = NO; + PRODUCT_NAME = "cocos2d libraries"; + ZERO_LINK = NO; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_PREPROCESSOR_DEFINITIONS = ( + DEBUG, + "COCOS2D_DEBUG=1", + "CD_DEBUG=1", + ); + "GCC_THUMB_SUPPORT[arch=armv6]" = NO; + "GCC_THUMB_SUPPORT[arch=armv7]" = YES; + GCC_VERSION = com.apple.compilers.llvmgcc42; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 3.0; + ONLY_ACTIVE_ARCH = YES; + PREBINDING = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = c99; + "GCC_THUMB_SUPPORT[arch=armv6]" = NO; + "GCC_THUMB_SUPPORT[arch=armv7]" = YES; + GCC_UNROLL_LOOPS = YES; + GCC_VERSION = com.apple.compilers.llvmgcc42; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 3.0; + PREBINDING = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "CCMenuAdvanced" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D6058940D05DD3E006BFB54 /* Debug */, + 1D6058950D05DD3E006BFB54 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 506EE06410304F0100A389B3 /* Build configuration list for PBXNativeTarget "cocos2d libraries" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 506EE05F10304ED500A389B3 /* Debug */, + 506EE06010304ED500A389B3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "CCMenuAdvanced" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/CCMenuAdvanced_Prefix.pch b/CCMenuAdvanced_Prefix.pch new file mode 100755 index 0000000..3aa7941 --- /dev/null +++ b/CCMenuAdvanced_Prefix.pch @@ -0,0 +1,8 @@ +// +// Prefix header for all source files of the 'CCMenuAdvanced' target in the 'CCMenuAdvanced' project +// + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/CCMenuAdvanced.h b/Classes/CCMenuAdvanced.h similarity index 100% rename from CCMenuAdvanced.h rename to Classes/CCMenuAdvanced.h diff --git a/CCMenuAdvanced.m b/Classes/CCMenuAdvanced.m similarity index 100% rename from CCMenuAdvanced.m rename to Classes/CCMenuAdvanced.m diff --git a/Classes/CCMenuAdvancedAppDelegate.h b/Classes/CCMenuAdvancedAppDelegate.h new file mode 100755 index 0000000..126fdf0 --- /dev/null +++ b/Classes/CCMenuAdvancedAppDelegate.h @@ -0,0 +1,20 @@ +// +// CCMenuAdvancedAppDelegate.h +// CCMenuAdvanced +// +// Created by Stepan Generalov on 14.04.11. +// Copyright __MyCompanyName__ 2011. All rights reserved. +// + +#import + +@class RootViewController; + +@interface CCMenuAdvancedAppDelegate : NSObject { + UIWindow *window; + RootViewController *viewController; +} + +@property (nonatomic, retain) UIWindow *window; + +@end diff --git a/Classes/CCMenuAdvancedAppDelegate.m b/Classes/CCMenuAdvancedAppDelegate.m new file mode 100755 index 0000000..672c36e --- /dev/null +++ b/Classes/CCMenuAdvancedAppDelegate.m @@ -0,0 +1,159 @@ +// +// CCMenuAdvancedAppDelegate.m +// CCMenuAdvanced +// +// Created by Stepan Generalov on 14.04.11. +// Copyright __MyCompanyName__ 2011. All rights reserved. +// + +#import "cocos2d.h" + +#import "CCMenuAdvancedAppDelegate.h" +#import "GameConfig.h" +#import "HelloWorldLayer.h" +#import "RootViewController.h" + +@implementation CCMenuAdvancedAppDelegate + +@synthesize window; + +- (void) removeStartupFlicker +{ + // + // THIS CODE REMOVES THE STARTUP FLICKER + // + // Uncomment the following code if you Application only supports landscape mode + // +#if GAME_AUTOROTATION == kGameAutorotationUIViewController + +// CC_ENABLE_DEFAULT_GL_STATES(); +// CCDirector *director = [CCDirector sharedDirector]; +// CGSize size = [director winSize]; +// CCSprite *sprite = [CCSprite spriteWithFile:@"Default.png"]; +// sprite.position = ccp(size.width/2, size.height/2); +// sprite.rotation = -90; +// [sprite visit]; +// [[director openGLView] swapBuffers]; +// CC_ENABLE_DEFAULT_GL_STATES(); + +#endif // GAME_AUTOROTATION == kGameAutorotationUIViewController +} +- (void) applicationDidFinishLaunching:(UIApplication*)application +{ + // Init the window + window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + + // Try to use CADisplayLink director + // if it fails (SDK < 3.1) use the default director + if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] ) + [CCDirector setDirectorType:kCCDirectorTypeDefault]; + + + CCDirector *director = [CCDirector sharedDirector]; + + // Init the View Controller + viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil]; + viewController.wantsFullScreenLayout = YES; + + // + // Create the EAGLView manually + // 1. Create a RGB565 format. Alternative: RGBA8 + // 2. depth format of 0 bit. Use 16 or 24 bit for 3d effects, like CCPageTurnTransition + // + // + EAGLView *glView = [EAGLView viewWithFrame:[window bounds] + pixelFormat:kEAGLColorFormatRGB565 // kEAGLColorFormatRGBA8 + depthFormat:0 // GL_DEPTH_COMPONENT16_OES + ]; + + // attach the openglView to the director + [director setOpenGLView:glView]; + +// // Enables High Res mode (Retina Display) on iPhone 4 and maintains low res on all other devices +// if( ! [director enableRetinaDisplay:YES] ) +// CCLOG(@"Retina Display Not supported"); + + // + // VERY IMPORTANT: + // If the rotation is going to be controlled by a UIViewController + // then the device orientation should be "Portrait". + // + // IMPORTANT: + // By default, this template only supports Landscape orientations. + // Edit the RootViewController.m file to edit the supported orientations. + // +#if GAME_AUTOROTATION == kGameAutorotationUIViewController + [director setDeviceOrientation:kCCDeviceOrientationPortrait]; +#else + [director setDeviceOrientation:kCCDeviceOrientationLandscapeLeft]; +#endif + + [director setAnimationInterval:1.0/60]; + [director setDisplayFPS:YES]; + + + // make the OpenGLView a child of the view controller + [viewController setView:glView]; + + // make the View Controller a child of the main window + [window addSubview: viewController.view]; + + [window makeKeyAndVisible]; + + // Default texture format for PNG/BMP/TIFF/JPEG/GIF images + // It can be RGBA8888, RGBA4444, RGB5_A1, RGB565 + // You can change anytime. + [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888]; + + + // Removes the startup flicker + [self removeStartupFlicker]; + + // Run the intro Scene + [[CCDirector sharedDirector] runWithScene: [HelloWorldLayer scene]]; +} + + +- (void)applicationWillResignActive:(UIApplication *)application { + [[CCDirector sharedDirector] pause]; +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + [[CCDirector sharedDirector] resume]; +} + +- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { + [[CCDirector sharedDirector] purgeCachedData]; +} + +-(void) applicationDidEnterBackground:(UIApplication*)application { + [[CCDirector sharedDirector] stopAnimation]; +} + +-(void) applicationWillEnterForeground:(UIApplication*)application { + [[CCDirector sharedDirector] startAnimation]; +} + +- (void)applicationWillTerminate:(UIApplication *)application { + CCDirector *director = [CCDirector sharedDirector]; + + [[director openGLView] removeFromSuperview]; + + [viewController release]; + + [window release]; + + [director end]; +} + +- (void)applicationSignificantTimeChange:(UIApplication *)application { + [[CCDirector sharedDirector] setNextDeltaTimeZero:YES]; +} + +- (void)dealloc { + [[CCDirector sharedDirector] release]; + [window release]; + [super dealloc]; +} + +@end diff --git a/Classes/GameConfig.h b/Classes/GameConfig.h new file mode 100644 index 0000000..b919514 --- /dev/null +++ b/Classes/GameConfig.h @@ -0,0 +1,45 @@ +// +// GameConfig.h +// CCMenuAdvanced +// +// Created by Stepan Generalov on 14.04.11. +// Copyright __MyCompanyName__ 2011. All rights reserved. +// + +#ifndef __GAME_CONFIG_H +#define __GAME_CONFIG_H + +// +// Supported Autorotations: +// None, +// UIViewController, +// CCDirector +// +#define kGameAutorotationNone 0 +#define kGameAutorotationCCDirector 1 +#define kGameAutorotationUIViewController 2 + +// +// Define here the type of autorotation that you want for your game +// + +// 3rd generation and newer devices: Rotate using UIViewController. Rotation should be supported on iPad apps. +// TIP: +// To improve the performance, you should set this value to "kGameAutorotationNone" or "kGameAutorotationCCDirector" +#if defined(__ARM_NEON__) || TARGET_IPHONE_SIMULATOR +#define GAME_AUTOROTATION kGameAutorotationUIViewController + +// ARMv6 (1st and 2nd generation devices): Don't rotate. It is very expensive +#elif __arm__ +#define GAME_AUTOROTATION kGameAutorotationNone + + +// Ignore this value on Mac +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +#else +#error(unknown architecture) +#endif + +#endif // __GAME_CONFIG_H + diff --git a/Classes/HelloWorldLayer.h b/Classes/HelloWorldLayer.h new file mode 100644 index 0000000..4ffee80 --- /dev/null +++ b/Classes/HelloWorldLayer.h @@ -0,0 +1,21 @@ +// +// HelloWorldLayer.h +// CCMenuAdvanced +// +// Created by Stepan Generalov on 14.04.11. +// Copyright __MyCompanyName__ 2011. All rights reserved. +// + + +// When you import this file, you import all the cocos2d classes +#import "cocos2d.h" + +// HelloWorldLayer +@interface HelloWorldLayer : CCLayer +{ +} + +// returns a CCScene that contains the HelloWorldLayer as the only child ++(CCScene *) scene; + +@end diff --git a/Classes/HelloWorldLayer.m b/Classes/HelloWorldLayer.m new file mode 100755 index 0000000..53db93b --- /dev/null +++ b/Classes/HelloWorldLayer.m @@ -0,0 +1,63 @@ +// +// HelloWorldLayer.m +// CCMenuAdvanced +// +// Created by Stepan Generalov on 14.04.11. +// Copyright __MyCompanyName__ 2011. All rights reserved. +// + + +// Import the interfaces +#import "HelloWorldLayer.h" + +// HelloWorldLayer implementation +@implementation HelloWorldLayer + ++(CCScene *) scene +{ + // 'scene' is an autorelease object. + CCScene *scene = [CCScene node]; + + // 'layer' is an autorelease object. + HelloWorldLayer *layer = [HelloWorldLayer node]; + + // add layer as a child to scene + [scene addChild: layer]; + + // return the scene + return scene; +} + +// on "init" you need to initialize your instance +-(id) init +{ + // always call "super" init + // Apple recommends to re-assign "self" with the "super" return value + if( (self=[super init])) { + + // create and initialize a Label + CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World" fontName:@"Marker Felt" fontSize:64]; + + // ask director the the window size + CGSize size = [[CCDirector sharedDirector] winSize]; + + // position the label on the center of the screen + label.position = ccp( size.width /2 , size.height/2 ); + + // add the label as a child to this Layer + [self addChild: label]; + } + return self; +} + +// on "dealloc" you need to release all your retained objects +- (void) dealloc +{ + // in case you have something to dealloc, do it in this method + // in this particular example nothing needs to be released. + // cocos2d will automatically release all the children (Label) + + // don't forget to call "super dealloc" + [super dealloc]; +} +@end diff --git a/Classes/RootViewController.h b/Classes/RootViewController.h new file mode 100644 index 0000000..7bafa7a --- /dev/null +++ b/Classes/RootViewController.h @@ -0,0 +1,16 @@ +// +// RootViewController.h +// CCMenuAdvanced +// +// Created by Stepan Generalov on 14.04.11. +// Copyright __MyCompanyName__ 2011. All rights reserved. +// + +#import + + +@interface RootViewController : UIViewController { + +} + +@end diff --git a/Classes/RootViewController.m b/Classes/RootViewController.m new file mode 100644 index 0000000..592e326 --- /dev/null +++ b/Classes/RootViewController.m @@ -0,0 +1,152 @@ +// +// RootViewController.m +// CCMenuAdvanced +// +// Created by Stepan Generalov on 14.04.11. +// Copyright __MyCompanyName__ 2011. All rights reserved. +// + +// +// RootViewController + iAd +// If you want to support iAd, use this class as the controller of your iAd +// + +#import "cocos2d.h" + +#import "RootViewController.h" +#import "GameConfig.h" + +@implementation RootViewController + +/* + // The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad. + - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) { + // Custom initialization + } + return self; + } + */ + +/* + // Implement loadView to create a view hierarchy programmatically, without using a nib. + - (void)loadView { + } + */ + +/* + // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. + - (void)viewDidLoad { + [super viewDidLoad]; + } + */ + + +// Override to allow orientations other than the default portrait orientation. +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + + // + // There are 2 ways to support auto-rotation: + // - The OpenGL / cocos2d way + // - Faster, but doesn't rotate the UIKit objects + // - The ViewController way + // - A bit slower, but the UiKit objects are placed in the right place + // + +#if GAME_AUTOROTATION==kGameAutorotationNone + // + // EAGLView won't be autorotated. + // Since this method should return YES in at least 1 orientation, + // we return YES only in the Portrait orientation + // + return ( interfaceOrientation == UIInterfaceOrientationPortrait ); + +#elif GAME_AUTOROTATION==kGameAutorotationCCDirector + // + // EAGLView will be rotated by cocos2d + // + // Sample: Autorotate only in landscape mode + // + if( interfaceOrientation == UIInterfaceOrientationLandscapeLeft ) { + [[CCDirector sharedDirector] setDeviceOrientation: kCCDeviceOrientationLandscapeRight]; + } else if( interfaceOrientation == UIInterfaceOrientationLandscapeRight) { + [[CCDirector sharedDirector] setDeviceOrientation: kCCDeviceOrientationLandscapeLeft]; + } + + // Since this method should return YES in at least 1 orientation, + // we return YES only in the Portrait orientation + return ( interfaceOrientation == UIInterfaceOrientationPortrait ); + +#elif GAME_AUTOROTATION == kGameAutorotationUIViewController + // + // EAGLView will be rotated by the UIViewController + // + // Sample: Autorotate only in landscpe mode + // + // return YES for the supported orientations + + return ( UIInterfaceOrientationIsLandscape( interfaceOrientation ) ); + +#else +#error Unknown value in GAME_AUTOROTATION + +#endif // GAME_AUTOROTATION + + + // Shold not happen + return NO; +} + +// +// This callback only will be called when GAME_AUTOROTATION == kGameAutorotationUIViewController +// +#if GAME_AUTOROTATION == kGameAutorotationUIViewController +-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration +{ + // + // Assuming that the main window has the size of the screen + // BUG: This won't work if the EAGLView is not fullscreen + /// + CGRect screenRect = [[UIScreen mainScreen] bounds]; + CGRect rect; + + if(toInterfaceOrientation == UIInterfaceOrientationPortrait || toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) + rect = screenRect; + + else if(toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) + rect.size = CGSizeMake( screenRect.size.height, screenRect.size.width ); + + CCDirector *director = [CCDirector sharedDirector]; + EAGLView *glView = [director openGLView]; + float contentScaleFactor = [director contentScaleFactor]; + + if( contentScaleFactor != 1 ) { + rect.size.width *= contentScaleFactor; + rect.size.height *= contentScaleFactor; + } + glView.frame = rect; +} +#endif // GAME_AUTOROTATION == kGameAutorotationUIViewController + + +- (void)didReceiveMemoryWarning { + // Releases the view if it doesn't have a superview. + [super didReceiveMemoryWarning]; + + // Release any cached data, images, etc that aren't in use. +} + +- (void)viewDidUnload { + [super viewDidUnload]; + // Release any retained subviews of the main view. + // e.g. self.myOutlet = nil; +} + + +- (void)dealloc { + [super dealloc]; +} + + +@end + diff --git a/LICENSE.cocos2d b/LICENSE.cocos2d new file mode 100644 index 0000000..9800e4a --- /dev/null +++ b/LICENSE.cocos2d @@ -0,0 +1,23 @@ +cocos2d for iPhone: http://www.cocos2d-iphone.org + +Copyright (c) 2008-2010 - Ricardo Quesada and contributors +(see each file to see the different copyright owners) + + +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. diff --git a/LICENSE.cocosdenshion b/LICENSE.cocosdenshion new file mode 100644 index 0000000..124035f --- /dev/null +++ b/LICENSE.cocosdenshion @@ -0,0 +1,21 @@ + CocosDenshion Sound Engine + + Copyright (c) 2010 Steve Oldmeadow + + 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. \ No newline at end of file diff --git a/Resources/Default.png b/Resources/Default.png new file mode 100644 index 0000000000000000000000000000000000000000..8710d78ac8a00a3ea05bc35c3a3a1d568001a5a8 GIT binary patch literal 30635 zcmb4qWl-Bqw09^4TC}Cby-?h>IK`a;h2ZY)uEn8+0>!OBfkFsQaCa{j+$BIDxZ9iO z&b%M)$2${}%x3?)M}EilEHPhHu~U?IO_y+Z+HW)lMdm}<6CQeVE*WD@aLEySO=9**aJP06vR3n$}-6 z_X$O>S1%-$qCmNM;zI zgoLAXdL({QdKK~>AmJA;SqzYnMG47Cr`H7hehDxcF*n-+d|?BaFa+;T1443cGW<~h z`oCxhP<|%@sNY#eO9O0#0p%0gu`&Q1PQZIBrCve6JR5*h0ca%;sA>Roj=#aG0ldNn zaH>Z~Facir155^KX?+0U8G!e)XF!otmWnqpW~8JtYDAjo1Z6_?U$MDj0D}d_=tFEV}$kF^E%F|22Y|M@Oys|c<%C_Y-I9(HZ3gd zY;Vsi_DbrT4g&+9thi*8*1Jb9l#9qGl3s_KE$=4C zJv3lFKTu46=v6Rl(qVqTrH=0!FP-`QoV(~ljARm1-^ZzEi_JCGdsMMCK0uY77>a)) z+h0R-)GH;1H##1jo7VuqS&MVm4C5=*5Sy@#3Ge4)@khDe9|0j&3Q4X2fRQvE``6)G z@qt$Wfb{QB=Bm$R7hS}xo#^CUXv11?P6>Vef{$D;H!70G>g$FX0bZ7@vn$7B5r89 z++0yQ@~pH_{DgNLZ~UXBdIjk4W2Cqlntr}esQ&YH z3V%v=%1MKEpS3iLSDutMd1%qXz6L|^7b`W(V8nLLcF#7?HrY1KnLcKYnS|?~8_i8d zjbS@QlASj@ygTTY>D-^S3NtlU{^oyu%a@=h3C@RTRH{e)eaGvWDL58JsGw8?E=ZXs zwIQ;(wyoUrXnwnrLaCbe4>&clFSnPrhkD_SjT1=_HV|#iNRma2L2OOpO6o0Q-BMe4$HvHaMsdchA;;ZBo1SyD5lV-kTG+7EiEf9TP?%VI@cQcnow$_8CW5! z#iO}Z`g$f6q*a#l1+Gb^X%9~uk%;&KS8>WG3dHI$~|gQf7E|h8*jDtQ)8Kv z)cUZ}*yDgAJwy3=>8r;U0=$@8(jO^W;zY|z^97}N6nTKP8t1>P#4)zgU2n2IRA0jw zOVZARVprcEGi@@F74s@LaakJJm!+nq>VV`y>L59I8-K-7NltN_OpES)DDLM0MFs-~ zIqQURv>ce`g62vIW(lE+sL*8vdzpO^S$>POcZX96B0aa=C?r?exn$M14R%K!$np5~ z(ebY2QVv@Oy%w7eeF=MmnenP7!*7xHxu(bTab4nOue=_npx9w)$A<5w?h^<-AeeJf>V-9`}DegHr42JCovAc`2Os zYvn;|Qfi^r^)OlvbB;tHc&@6U$h+*?^q5HOO3W>9OKeL-V3m7S*LYN*vth7dM6Jg; z*EnKLDwy`!;@R(c5>OKI5ycucCiHWt{)_t?zW|z!cyXa6%d47&qtD-eH+?4fobV0& zZKn%NV>5X86HIdrOCL`hx5bV460`^Xhhe&F8rKtq%@7e40gM!ncZt>^o#aa6^PXIm za$l3alTMXh&9dfF6Rc!Y}a%hcxRMiQhbQVGaLp|jgoZYNkH;_ADE zxkZ4+5~+<|F47naVRoEV104a=DuUYR{Z#Bjjk<=vb=9NT!?Cn5cOnAe%X3GDYC0Uc zYxSmgkvU{y1TnD{@0Wd%9Q_IqJ z({P<|78dJp*0k%6F0W*kIMT)#ms-LgQcZ;&B8{z+f6dP(^(n8TvnYEg=yjQrdy%w*CDciqEy>~POt2(+K4WLe; zK4Q?~Lgt!gW+|pbziK2vS>STG161u#Img~?IQ%$TKeGEbXn}i;$`aQ@kQQ>9tisb3Sf<jD>E{pTJZwwIe3RN-UPA={@cRe=goFVAx6jDWeE`6d6970c0RV*30RSTB zB;!6=0KfsNApQBP&*EX5e;vWKC#=akr&~ zF+7E;+WMTCUfxLruZ3#qPkZC(^hJk6L`*C_ty6_bfBN+mmM4-{fuR?%-#Q(I`yn6R z)5yH#-ZYgk_!nr5fM+kB%V|9xT$gKlQl{O{FWr=><_N)NL>t5|SNxx^oS#pepN|Yj zmdIX6zQQSdMxid4CveKYsXTl0rkkj@664b5ROimQRdbPJRcVP<9CcBt%a4vsAD5Q; zHni!ZsFKBDwK$t2liJS*a)ktiR>eTZqnKx+%qR~>2WUqVs5*aL!DQ&7AVQ05iP@9> z*7o0Gw9KQ`x`|*Q5t%-T+sM(ER}5RPe!zIswYYa+IEs#6le=9WF2)S^jPr1X(tNo* z2>ssC$Ye-kHOFp$I9M+JoVC~fLy;jndX!DSFH*?PCW`!ZpH>#NN?G@t!~nje#?BZ? zA}zn%=mL8IKdl>96d5YCm(ednHWD|aKD@+Epq6}D0G|#u#(Y^-Gq1Ntu*phc`P*Kx zV%s_`l#tEXR9ia30o^!w4{rp^O{fu@HscJ#haiND$o$BpEV^k4pA4YmP24V63>CRX!d~ zuwb$`9l+_8m&lp8`O_2-9&Q>%OQ}a}vH~IC$w&|drz9aa} z_zg_|GBL7Zm$gyg+$>P#jIKCH}#Twd%ZF~u> zowCPEa-@@s+D`(xfIi#fXC+1NMyG)(HIz-PYVloCu#!TuIS1&JzA67|<=*5M5sUxf zNK>(`+Cq#jPS)nu8;7531=c8kQrC~_?9wC=yPAZ%VQjhlPah=@ zm9hu9sv?Z)v|Z^gn*Z_90jUU~bwq4k+_38T-QiO0N>==F=Kv-Ry5GbswV!J~3`(Q- zzxn)1T}z9uRL^Rp-$evBo3XKS#3IQ(l}lRxUtBkw-(oqx*~>$n!j$`Wef6-+F9%PY z*qyd$`q4zt_o-(YMa6C%i#iFm(Vs=1;$z2te)>QNkh=UKrZ8jkk-d(~D9#N!tY=^w z7d}g*_fG`|6_sJas`$>Uk84y~PDd9C!m-NOp0Cw@x$wo@5ulK?*$7p{iAkQ}eVu~a z6cc93gvA!oo|OIJZ%UHwg`Jo?X7h{VUkkV-cFaxLVc=N0)Lq8p7=g_3x{J=wHFSgM z{jQDn^3^6fY(epwQ(Qf+@vNAUG((aj$@D%8`ODbJgW|%@q z2f6wM8C^F@lfwyecYe8X?;_lTr_E&YJVaD(qSdzu@;6)8=RNLK{ zl(n5VR4?!i-u=8hwBP3=pZ1ix#;#y|EEqe^=hKiW0`-8B))+94?0(Y|&m1W22u+ebgngP^Vy8{ce^9jE_z68}f)OYLVEM4@>Xe zCbJn*w^l5N7Yi`$GnX{8dyFB-2UP^;4|-VzIe1cFjafMK?+B<#$TcLjDYpJ@$l?0t|uzF1TJE#6?yr&ysuZzVwh+zqLyC|69AJO8@X<*k2OMN%# z;5lR#>hh(F4j3UwuS;kl+@B=Jh93II_uJ25mp!JD*Wm3oi77K<9DSAff8_V|Q=p4A zSFB2-q#X%hF0lNGX!@sR>Ou{Vy@82F1CW=6g#c-B44t-N5+9lfxU$20RU>IA-^G8y z&>c(Ou()tM13?1#@gxRSD!Degw2f_D;xLf( zZe~qY;i1m03&CH&9HQHxD|JoU#CO9~D720+jhO%2N-~2VKcDCoSLIx4A#KhouddFu zWjvv`pjfRcN0zA5z}t;0ZNdI8Qc#DyVkpxNJ*gPSJN(^jXo2VChd)qb&;pU_d;rik4Z)N^T8cQoQUp5M)7>($$p*Q0? zP4;sqk7}pf*o`vJ{gSOAGna|giri_sXY5cDu^f8!{hiz*P4}sC%qMi{K+vQHA6lk( zIckQgbLcDBgU!cWtE*=vLKSk8ezW@1+tv_l z+1vDb2B8H?W(>qoqz5NjN3S{0r8FMS!PLz^lp&2yEy;6yOaj;h~J%AFmTU)Kw z!}XCOPevi?FI?V={x7j+!H-zsoD|atr6&2F_oji$p8~+g@`wVdAhh-~>gsAlM%d~_ z|D+zjKG3WRj5^-0%_67Q{x7LdUE-i248Q+_E}F)c;D%}mCo9J(%eRf29O_~DC23-b zMN|bs(o0K8%^jeY5--Ct94mTppN*we)g$rN5`@}e6Dmk(fNCxkYw4|*S)a$5ZQ~(b zLW(WLt&UQ(Lw)#xl__{W5p|oN)N%)y!(UEh8!Bk_HP?utd7KGHVSRj#Qh#<6#J=J9 z=XWx%mWXE;bKmgS&p$T)n8*LkKrb|pH;~*X+@MP#P+?HMR=~MMlM2#8+5{2zRMt24 z=6uU=q+R9gys#m$nv@jH9kVyd?&q{Qt>QL#>6ZVIkyfht6`Tz;OCe_Gu=iB^D}diX zpBKt2SxZP-XGt!hegD<3epdT%-^MB(OzQVpMU25R`I`mFR{GpLj`oUeim9)Kd=8I1 zcWU!1rRTmz`ss@TG{$xz@3l>FkRoGBK>Bo8mpu2-YITIsTp0_l$6Jk5W()HX$1A%R z_;hgM!b5ciMBPlCuGJ@9)z5?~>gVMae+s4OE>&x^iiy}>SX_LLV4e+zax3XsVL9Y; zglIchW;N{L8?{Ll1;(fsXMmdl^N0neq9jShk4&=M48*S@$NnUbC88_2%5^tR$E7k0 z1^9E8-cJlWpeZF14#L_0=2wDeg_+}<{V+af9W=WEu{reXNm;!&sdviapb}ro^12Q< zS$H0{jkFa8LCV*BHM)v)--ibDT5b5SuZ~cv#%n3h51b%HzcpS5Qs{!d`WfyOgAa%M zwG{}JB6SCJ+{y@;pd?UtJrk_RcR5-zI?n4AxVLsoZp4Ses3wN6FC{X`#x3U#7aAWUR?C^+{$Usq}qclXxJc6-W(SM796y<%kj~Y#@t*{|;nQek%Xw&z&jK zvD6tB`RG)=El>6V+oU~h+pNTIa!N{^40G>`c%5uV^TY51OP>MBe*I<6n47+(Im?!~ zJudIy2rfPGh{mCb4sX6D`gfG<53H9pnxtYSj1ZM(QQd?bl&aa_ahi?;7f!~&{FDUO z$v8$ej<0rqc7h!oNFJ@XYN`!@!&(Sp!pQ;68D%!R9JbzcY`tV*M&thG>ryXBER|og zn;xnO=-J`g7JP;U@f`m0=%%qh+ipgm3Z@jvA}--}V22ku+Ts2YjtR5bf1!Ci?Y z%+i;j#9>ABaFn_OC4WjRz#CDr>UaP#>v&V!tbOdm3mq2Tk$)iJQN~TQ;xzDXdu^-0 zSelo?ke{#sT<3czUUBm7PnpW^2bmKzi{iMN{#yA>doLD0m}0c?do0Y`Db=8Dc|^W- z@We_-c`5hX?wX71quoVU(l>3=&0m-5Jhu}SS*e~iuB2@h$)>*-%CJIk)<^&|Z)y6{ zRO!h=Jnt&R-v8Q^X+bbeH75OCPI~mPlWq+DfSKT+$kFS6A zkRUnnKZ!HJ3K=1IGRpIn7Gu3#r+aDNWAax_tefobe};{=g)A z;d!?rw;5mg3j6ZqdYyKOZlmAo>a9_>DLS?#u~%GlbNjy>T{23^aGQxFhq>EAf3T4a zX-7`(45v2PXtqju`n(<~N&V9l!X)Rn9_x{l@4UnEJ;nGd%~DS6z9opPha^x5rY=aK z!y6dJ%J3oN@~dTUW&qLtWHV2}*oD2D8Y{$)D|`1#TFjn?k=7O>jEPgi%}GW^ zhC15c-w&v*=K5bF$btXc$p2sFzo!yKpNPcwp6gKuo)9GuT2)+~SQC)PL$EbRg46Q~ zk=IjKL)1XYEtaWy2oZ7TM*qg~!=GSS=wt9DX)uEW_Q^L3AD;lf3yyepgsKT{zF@u6HHx9-X0q*aU*MC28+< zb}@U@ao@fAxVEuPh&sw23)#Xa3~oVqE-&U#<`NyCi1V*k7Of<5rim3$3SB z#F=-!&4{54U1Eyo-pu}nN`eU-A}~kr z=Ked_D%Da0Ow-U&rC#V`2F_e=P372`6Vt%9Xo#T=#P>aV9#!Vc5G~OhwjVfvlf<2; z{uEVjA?z4Ia37#{C7pBr{REa&@yZ_B(A_xICj!t?Dv#ypt?*RdLi1wllhOiri$>i_}Qh8UQ z98c4R9j*EFTrMg`_EuTUKEd&!3%hA4I0hK~NhR7|^$mOsPFF{wPb&?)&61l)WnKOU zAm?-xD^S{hxMtK>@f5=ilWOtw^(MxSf-W5d26&n)h2?A@@wD#mnPAr@pe$D?~FYpeB|p;0GnEXGs;u(=!D1{Ex;Qp!W^v{5(>g&;B!mkK3D=f zE8sZM@o{lHrgY!st3s`NPfIk+(76bk5#5M)p%A67{5G_IzxcjV}N;BZ;sq^xv-+MJktak%12b`Af!!pT?< z`*}qCZbW8R-W^-ycu$37e4(-IF%+i_)qHxoPvShgFBgT(4Q)cdwsq-1zS4=qgV#^I z^^+#{U)9r-hiVy~*D(Y>8VRkmKXfjC`Q1f$aewozLW3z`0B~VpoOc&uvp^19eJLfL&*tYVc)xhllhAmLQB8Q z`Y{lPD?UmsTIK>3cE^s+FG5a2(pJe|;qCp3pV*Ygiqs6)5A+wS|IHCoaYZgFO0rI7 zzA=>*mGErg$|#$BEZS(P6>URTrP-G_RN!;<4(^AaZXU^jt>RRNLM}8#F_I})sixI^ za@=0^uVbPDQcR)mTXmO_YL2uu@_0 zYWKs#%h=C#c@3}q25Of1>WHnlW6VsCa8R)l+a>wfRtD%Q-x{Fe9mj%=OR=9~c*I22 z)<|Mxkv6=Ea)VS0TP6YA@M0LScD=Eq(*8Jc-5F`5lEJ6HI!WlYs#}Adhjgx=?p8P` zLlOdDqXi|vmz`}#Diw?Zh2VpIWpQc?V!}$ccr2?^K=bpmJC>5!tmE^;VfPo=$PuzaD7uj2|&idEF*i%`g5sd zm39V9W_vRvMiyohcLsm7;fS&Y)FbyTzFMsoQBu6^{c#7u39bils$EmLuUQ7=Xwh?MgO`9r{jEFd}tP@R8$=foL8EwYeDshwaydi z(^6-{8nOD~mXFyq>3s-;StIvb9B_S8DY(ftpe(>rBQ62y0&X6$u4t)lFUkaO#0-tk zPBm#OI|RA_96@lq%e?1qIf&C?c2+{mb6iRJz5i0ywr{JY<6|uJE@vBQ7N^%dDTI+> zJtY-7nYL|DtGy#?POYKA3tJ_!V)qQrcxvw#B8)~a+8?(21q-iPCJa9X+4U~3ZTsUn z=%_Quu%Ugn{E&URK6`{_uW@%%npi2~P4id}Loc4dnE}1>h4+uAJ0Y|Ptn#(BmFk=hX%rltN1O_+0|M&0avK1SKZwhc3^OE;A z>+m%@8H~X@xEl*bw67M?b}M163r-sWd~QmowT)eo0#GZ5SHR`e#z$XBpdlaJ;AF~y zj}sJUj^ii?pZBg8+)D9u#{J6`6*6g)71n>yBakefSLVX+%s+*vq01dGbc?d()(@X$ zF`$EkYHm~y*t!oTYqgw^yazw;Uze*L-DG6Je>F~-nN$ZldIZ=!_~l3w1DuXe!3287 zzKC!8A<-u{3x%jk%s^y(qVq=*1_hlb!~G7@8k1hEo4Co6Y&G$IDmW_5N8Whx+%W~w zviX0!yedG3WW~e(piyi(=g}}De1YR=X4fx$SK|9^qvkw(%;biB=~oX@gBEf~mAt)e z*Pb)rMIF`7KX;&b<#^U6_$pAUmhh{}_^1&HU<@JoaAc+Z1HCxP3Ltsl&I)rX_JD5#gk-4bhF-mya63 z)u-5ZN1ZNSzP+3fJW8~x##YkAA&vkD$*LHS+ZWNSEjax-($`?Z+6{~K)R zAZOA~FFU?2Wp*RxLrD$sy02;Q6?g<%^P|Zl7e(SbF<5t%ZF4@gAHB=vO8(Q9^z--l zI?$hsj6&eerfG^d>oZH{l#2-ndjL1`x{_{V<#Bp4S!mz`u0`ZhDtZL&=mu96tE`mj`I-YW;kJxE1Mo*#ITdC zNjz?3p!=Db#p6${cfk(Ie`^j`)+3lLHwuJwyLxIaFe7X6&b2wI$rl$ZB(HFNSi7-L zesIBCo5l${h9lybU(AC>SJ;&NSIbz360b;1(0!g3ks2wNl3Tc+VS5F40=YOjUO5o{ z4_lq(=2`W6jW%K^t>v3@t4?v@;AuSHNKRA{j_vDY0u{kwK1=U-@Fa)D;e*p@XCQB_ zu6;`YkH{_wa#sUX1D0Fx@9VJ__#5?x?fidcmJYy2HI2|i8J&W z0_m#GP#o97#9q&>LSF#N9oVXf#sF)awegSI)E)0MrzQQv9>%g^qh&)f_8*p)b|b!C zlVZE;enb@^@^Uh1rau$bWX++_;Z!vrs2S#QuXz=Rtu4>(KZ)&v^Pd3>2OU>xo!yqq zMQ$hcAJ!f@N7wTKwFpULUHp^hHsv%%jd)QFc*rssoAgNRcS)<48<~xF`s*iM!PjFV z7>zZViYnnih~F6{h-eH*i)s+lbZ2W3Gkun7ghcHEY=@mRa?b0PwE92J>Un{}I%5m` z-cGvHDey3M9Net)P;S*}!$zaJ7V zb~Hia;4!{jihX|*Z4VdXQNY|tB4qfuKO63ZY|n_Gnu;Z$MnR|MzntIVyL~0sGn}Kc z3`wh_byPE6Nm2`vL6oig!kvuRW3v^{eaLmJ8X?Z}BTY8wjl1y|*3a9HEN##Wt%^xn znhu&aB=%TZ79+}MX|cu5pXzT9Wf}zX7h1x4Owb#dr=c$u5+e-uK^bNWgU-gzYJaWJ zCf>ngW&`MKZ65A?RHP7)-TutrrQPj6$JwMiaX(QihDZA@%b(7p{M<)V+PKLL(~t#) zCDVUVo8HGB%Z2GxcfE|iqRg)p#vYbo^z^baUNL z5V?XN@Q%5+blulJRbR1~>`~I#dBR5ph433ykRNyzGFj=G=VwnW0>l)#o@2HMVH^;z z#1DeOI{bvDX74BRsamXj8|>+dk#1z{4563E?-!`(HE2K2`Fq!w74&K%@dd}jNUq`D zS^Ne3SY@U+*vZ~Wu~POB>7zi@nsNRECB^mxCUeUXkQV_?CYdFKZrBE7a(8pj+f-%O zc|8)|<0qUct=b<9@IvbGN184RNY?wDpxwXuRs@;vkq@)z@U`4sq+q%@`lOQ)k@|4P zpj6~}LT;K;RWi~uD;%mjhDS58vzStM-Ekng?*q@;T-&FJ*PC@J4v<d3}!q z`2>S`1Au+cml{j*+HY<>x>YCriFPP112wy3A3IF*FEe*mzM1;EG?quu8o|Ez8(#h$ zIB4hEA-`;S52AXBP8M^gS>BoQvU36B=Sy^Vj*cHepMM3sj*RNBL={>anP{VKZJ ziVk(9u#a!YK8&(^?CfUl2u}c2-v-#BW!a~CM_JM}Nq@rjuKW={*J2vIFNq}CMe7O~ z_p#otmu4({p&Q$I-@KFxZt=aH8Fla?jc5q?e2}kO65UCzyeW+|;3lx~WOGnNkxoqm z*i|ggD_VD;#tKxpG&Nrpq~**sn92Qy=VnJX!1Kj?c{F7Poq;Vy{W z)LgE)wlD7)W+3JFIY<1H7@uK>10hfvh+QSPw@sQ*cAdTI%Zn?*$?qsM)#ixR8C>C5 z^b0C$9j`w%ryCAi&n#c({8GwWc@nOuZBn8_ggTvwaLk({DR+tKSsWLz`C@&Npr(?b zHa5e6z6+{;fwuelae;il%K!c$Ary&m$J*ZAmK|{EI611m7t~vIs=MPbuszDM?jE5d z#@z&PW4`?B?r?$S_6YrEV>+f#{?Q_DQ75S8bMo=gvd8(uv!Na-860pzzOuKp5N8!) zZ%L~ulf2;@G>+45oB$oW{Nj0k>+hc3t9P;s`%bMcN_;Mtcc$bLZ~bN4rtNc^X=bJeX+ao z`oq0Ux(;C-Wc_eAcow+|#iVg^J_|1#?+3uDR35>4s6=VkI-(4fxm35Ox2Ed*TH|li zDT%Y5Rx`TzR@ji00qj672kvw>m4!;8#?(h|ywJO%>J@+oJ4Xr(&p={6F=43<%3wDHG{mnC91aDv(pa$Eilib zrulRi9>;h9@>GI7QmzDcBUB3K#BcOp_?*i{=ki?y1&@}At7^wDGaLN66G=Z_+fz{N zNrl1WN=t#i;ToIch#p{lv`J~}ohCg&&4B+?3ygBPrYsZjV!Cg=3CN*K8`0k0K0xVz zIOp}C;c8{);jWd5;kK!M>0)ZUgLS4jHd} zmairthl95q8q|NSu(xTd$Gj`uS(ug$)vIfdnLgx;``*Ju6N_qI2|sCM&uG$v2X z#oD<~y*h4nfGYT5QjY5A-~f~A>S7h@Js?_JI}HrF8{Ze`5EkSzdz5)PRHM4>KMy`n zo9HMH$)PER$8r&RYPV3c z_UdBr-0&=1Ur+dX*65Sh;ru%_5!b_cuJgf@F&2}Xq6Tzjzn11^TneE&$0dFxzta4D z=u?Z+Q3w1`SX?adprx1;wbME1iU_gz?>VLNX&@$si4A;NJ$vp(N?#=Qa*1yFJ)TgU z6b?d?b?{5E<)JBB{HWfDy5$Ey6NkG!aU&jgN&nz`0fW#Tm9_?O$2rX3&pFxKX$j(p zd=11?EVcPxFVtFttZ!z|X?I;FcgM1feRl|GTG`<(Te-QbOHWU|afLm=a)Dy;$E}Iy zjkFGS9+6KZ4;EEE{t3n)GM22WU-9G>!m`6sgRP1^0oiXHBD;12A`RZ|Zgk9Tt$GGF zJnT~iuPQoANu5Ww9Cvu%JKJ3h6L(xK+WTJ}Rv(;{j+ievv#bg8=#aEkhyC+E4(wQ% zte?6&Y_9*GN89VQY~jeOAH2Hd+s<`O9pG_%5o;B}Gpnj{;sYNynUXi5CjR{eKt|BD>Fx+O(pV`DGB2)Uf` z6PWlliyr60)$EyfkKlwP2R@pW`))O}`OU>_%rr2?mX`(S@r-1leHQA({->c3B(Rlr zyjTzamoG)?jKpwJLq#fushFp)G8r%C)~dn9Ftz~iJlWn#hv&#dpt373I=!MLsQeSw z7y#&G@6RU1jk=ZcRl4IWp7Ji}H28stFJGXJLfGqYsevQTxqRXJ2P)DSs>iYf?1iJE zqL9?bCmk&}ov(L=(f8;FJ9)n7LROS%7(*u4|?Z@WO5z#QZyE z-O)qxny%Bp7rCPTS2_+1YPod>RYNBrn4E^{yht@t_qt(rJQRU9CmSm*-fMx^D?L3u zXNjV14J(VmX_s4iX?({%>oC5zsxjfw>>=TU2f23 zwB&m@UwyRHaD9F4oNzRlNK49}e}qI75?fZ$S%yJl`V!6dfoI!8PftNd7Gi$akL+@H zJRKJbD&ZVIcHf!WhvMkJ;6zEe$Cy%cEPr0vB0f& zAN#?^b24e$7TLPRy(eHAq?pvG9;w0QoqSgXYkY3N3cDZr z_s4d&M>BhTTcdJRfxJ0fnlc^UR)YuPFhA?lr`>V$fp}Zk!tr=s@NSw|sRn6ds|`=AmukIXd#cABuR4@H|c z8&m^so6ey#TT@)PoZO5jK2ZOMht03;c3&D|#?pMLOq7HHVl74Gf7p%b zLLLjW`bTY(i2Pw=IR**1=_0l2U-^fu>~(x$mxq&~ z9OB-qv$_!UGhyVv4Un)?!%AM6=!VwDoV1{3G{3V?EGZW0CMC_}d-k>HU3jyFv^ZgJ z#Ct_zTy9$$8^&%XM2MFe^j3Bw>lkS^NE9Lw=+k_qM|++{{vjakUY6d$gWfm|Kh@YP z|H#VF>SG}hxG$qEUbmfM<+h@=CkhW~B~b+GOAXEqP|u6YOIrmdUC;V=OLex%x47(O zU@Osi=fP(7>e2>tLD28#iKw~L`eA^zmI=5@Fzz93Ut}vi0!-jgu&!6LY!w)g z28REf;6HwC{coEb5WL^T7m$J(G9-HRhMV=Hs_!nK=bH}oJSDpEScAM;YNr}5(^w`$ z+U4b??k(4;`-)~c097JYAtFn_SzVx{s#a=W8SLdct9w}OMg6p@&eKqe z4*&gVn}F_npWhWAhpzU(d+zc5+HnksSw_Rp77z&DcD<^kK8Li4ogBgJzDNf^ODd`5 zttC>+y`GS6yK*61Ag!9pfBJ9iPo_k&6o zaSie71B@@y z+ph6Kxu?e4|qF)ra)6AlvoX*+W-(=~v45`2D}_kZiur%dxoZ zYih$?#NOP{rmnSqo&EN#Z~F3+wTfQEl^2AudJ10IA!hw_-s>6oBOQ@i2Xb=ZCQfG@ zi+OOvw^v_UT&1^{834g@o~)hqlXoRyZOis79h;E_IqCLECVl*ZTy8s?!qAeZD>-Lo zuB<50Qb0%G7(#^O)VBlM<>S@AKo+o3oxRVlR2w)G>qxQFsOe_>@X(_T1z}@bML!=I z6=e^XdA>L#t0*xNd-6W(NpZAITW;D&(oLG3J$PO%aozxNQLNW&uzm%8LBryBg?=;Z z?>|ZwJRIU;U0W##;!t|Gz$bh^o?zz>DK=+K)t*7H0!~JGCT0%Yt*uKYp0*N>u1i5u znPgLN1h+2s`pW0HhoH-ir{;P{R@fe-Ce0!=k)ToI?NfWp<5`q8E<*b8#VT91;}2VE&ewC#!yUR?eRln}hQRRf;W75YZs3W6%0KZ8Vpd|2Ra;&cqz@x|{K~CwQI3Td}Wg1#>be!ZSWz>*(-% z`*i<~qj~!pJ`tdwyoM~CMtsHiIPDRz;UBDkX|r1(M=h?&pXjQ{;S`XRoY7sbRD1D% zdE#td{z70yt;}^sR)Cst^~Q#2EuMD!@~ppeW75Qb^9UcWJP8a&0+4kTFPSBhoXfN| zwBUu>7ij{(Yr?cw&aFj;N|uj1=Nv7uLKOx9C3hw_F$}r?>O4j=+0Uzx$M9S-pNc4e z%|CGQ9v<8J?8QTG?@YUR@apC6 z^U31cpBWmfn4o$}@8WsP>!1W;p+~hIRZIFgoQmf_XL=&e6A5Ii@E+za6302 z5VFruC`!PdlEgwxN&YDqIiWoNxc9O&F!9Xo6)#T>6M>g^NW41&-p9rwp&TsE1b$dYzU+^8byh(UQyhHZJk&W>W zkHdO`uu!KDaU1IbA)Z7Xk;I7QhT~($ z$P)w!!vFed%pEm&flmX_k8D)U*V^v8j>HV&)>1|F^|$AF_bK-cP71f>E@&qy4XZTr zV#q2JvC;xQErY)d|6!DO@Y9I|S*84gAGi2HE8#8wmC{gfTi>C=Nx!~zH!};{@|gT_ z)5g@&+@U2|ebU-}htI)H>6wwT%R>i{!tMndabd6J58!eBiCi5WpDrc7?GoJ+hQ#K= z-ZbNCED?HqaIf0iH)y9y-(0+Jp7CU@PpF<(CQ1Q?;g;BrFI=SGGcU_-4}@ce@YxiO zHKPQWI03|vO}!_MT<+^@h=8caF&LWGNQ@qm!qOakUfc0Ao(sUs%j@lNP5ICt_dI7D z&{OnxA1oE z-emb~r?XXv_Z)^vG|`@_Nh%=T_)X=y$_s}W3w$-1aI9Pf^mN0$WkPnWld*SU#k7GJ zIri5p)ytXpfynCsg~!NtfS*B=P#qF;&v&(JPfh-aR(q*uLo%o>Trk`nKlr4u;pu9? z+3s_(&-uMrt4Pl3P{sn*Tx67Flm1-o=EiDnk3xFlCI4)q*?HbJFJCBW zmdN9vAN$feu}}Qrz8tjj`2lpMk(S@)7i58SFBw#rT>d6f&d&PNlin2>6ROI}Z2=Fn zznY@h;SQ(X52IuIx$aD_g8Ur+zJJN~<7y-Ol0|Qczw)N?ILHJXJ!}IK#zgj_nI0<) zVnCe2Hij7);Br^O=5B{n7QO#YN#?%M1FDes5~2`oeE!eF9{L>xm-+s4f2OejFm!YV zYb%uB3n(YsqlfEz^Kz;8c?b4)UlNY})1^>E43>6+%PMWnou!1@khdMoRSZOtUQ)!g z3iJO}b(TSK1=|)LT!Opn;0}R6fZ#9;?(P=cf(LgE?(PnQ1rprd3GPk;0Rn_zc_;VX z_v2Naf~qNo)2F*C8oI{5+g_<3{bu!PV7qJi^({W{jaxqrpobU;Lmh?vGX*AQ zn!4yD{3|c6CZ3U5m_zqX#}|A(;THaup)6B~@tbpsiE<101L_y`MzVH%<5l`y{O6Ve z;G}dmuRGkT5Pr7RQN4LqmLu>wz#nj9+t05H!r!rZWqk{qGzXR7`B5t67c4jn1B+r^ z``Y66!xQm4H>tl7q-e@!#Jr0VWg0n@h6+=7BfU#+=YI!YrIKO*P}IlMIygsY=&Svt zsy%?Yxh&910nFlh>z*#y2Y0^#Iql5@$+_%yJZyuU9Q7NaCHaUOfe%)vU4r_1gM>`5 zqY}q6=jXG5XKXR6Q(SV!+zDO`Gp0I&H5oD=%WA>5r1Iu+3Q{Y})dYyne{Y~HsLCc} zx~ogDrW!@}KUwCdlaJ80+PaRpQk@g`JofM{z-=FmW<$OoUI+Gf{?A=#d?eFDjnnSU zFJ3I(TY%xO5K0envGV?K^KbyTt48XE=ppV1)L~|I+#I1E2&YIv{}!*CZ-bo}-!rVe zR-1|42jFvXa_vm~+qYXypQ;orxVrdNQ&jY}<`|n)Q}~)IxG)V#+dd=yv#P^e;{%Fz zeBggJBT#SX`Wl%m$43Q!-;BWHRj~+it3fo#-!m&+(~T)zOx)$4SR(thJID9a1n@o< z^DG#1B?F|EeQzE#pSxqsLEAmtDUw{+WJ}t?t9B`~P_#2;BeKW!0>e2-O)o%vyj}c* zy|1zG@9yN3*wk3@COy0&cHNC-FkBE2RPwrCn;zxbzfX~Ii~r5r1-K+Of(&&Jo4<$u z9#L}|w+m=84A=O6nT+~9zXPePF-vqnXy2XR8P%ibuCI$%TKGM~*9EBK#g)Tvx9Da4 z>sl$TD{U`Yu#hTxqHno%U8DxhWV$^}(Ll;ZS(G_c&!gAh0Vqq`(?fTHbzHy11Q;W; z@gMh#AI5`b!`n4W0z6%7Kb(y_K%;o5PZ^+omFG!}0^rd>KK$Ye<_Q&^^WdPth#Q9{ zTC9loJ)R=27>3jlMsGhlwcnS-@?}IYZM~d65{?v42g$GWj-FR{_DSx zmGfXA24E`&8Vew9AqBB7f)>hATu6NZ0<3YDeIf%Wqz>1FVg2%Za?096@Km~e)j;ZK-gj)YYan0a{iv} zsUfgNz|eb`uh%ake@phtg=5>!`o<=UB=;RD$#|MX`D8m z=LWD)-E}Ri-3ii z&hPtoqDS{}`{n66@cHMqsKoevRqvYAK+E6L zW$nK+J+NV^%xpA~DmK^oIBytwBg+@kAweAlN@l*KXu|g4!U!Y3&>(ntbmpkf!0%!j z5FI+a?!v<5x!A)=;Qe>Dz`OH_k`&uYAhL(1^Z_mn+j{2(k+YS3U&Fx8vt<%8Q|ONk z_}&q*#k(HSmb*4WC8?6;e3YmYT5l@4kmI7r9a>-GmUt@PDC1Gj4U^ou2zmB8BC-wr zTy&r*H@+xs6g#u5y}hCsvR zp{xIX%-J@zy;uIch4r@}aIf5CWAPMdOnD(&p%T@wAKm*~(GwpWCjt?xF7EU$Dc z(UJzEZ=1?}n_p6lPB3PXRb$?lsw+BFrC#dcU9%y~afWn%xHy1V1FU~}mT=21trcezq~z&b)<9`_zX&uD2EWhY$n zjKmHWk((q&KY{h+O@&))v|~n@LyJ;(pk*K^gM{q|aSYFPxnV&c?M))%&vX+?kiSp+ zM!y3~Z%@|u#oh@E36XDHHWYWjz1f)WJWpujf5Yz`MQ_op9nY_&*~pq17nJ(vrcqV= z5sr7}iiav?5>#Ozj*|+(A%A9**?`XKU#7#Qf6^rU8mF28e+_rhQ*zsVJ7Fv;_UDt1 zwX#WG2d&A_`5XR?x%Hd59P$i)A`xua;aFNpZNeolqU7hMFXQ4wBJ2d>{rqdQeVoi9 z&5Su98^dfkyU)x69}O31r9U&5uq9EXim%g)BK|QeOr-8&jl#wu;}kv^gJa$L?Dgix z>jBsz1^iz7;V0F6T%+60T*6P}Vpa3Gt;TFq36lf!#7B^ilE4N*WnK|YAzB{$71HTy zNE4s6?rtMZ2^%e?DEkl>T4@0l0X*KwOb$4mze20VgLIsIsuvvtWLg0$wWxj3Oyty0 zfFm^u{iy1MlWbVyt1JPsU+Y|&Agk~t=ln#B!WdGM(s!cB;rww^sl9X6U~aF@;g=+; zO@%_)Dk?-}w(M&=tJ~W|RpvsS80rYb^t}qiNH`+&6BH5x?n`bO3=vdRcx-gU0|{{g zZ%EhyEny;R6-ZhfiP@Wd$lS5C?1umf)JdM6!)3vMy{a-2oPoj5saAzJl=>)nEt&WU z4a20c0!~=C)1!_A@wK4RzDy?joHuSc^mSTCb<>pGcIbty!(Cw=+}hK732W@o?;4i4 z!;%zJM2NvWiAd~-C&ZEm%#5l}VUiMdSok3*(qV{|@=1@p(uk}w_K*}8%mMu*$)dL* z#J;pBt{50sF~{c7AMpNa3FEN*WkzM@L5+Pw!<~TPB|CyV_sN?Ct+)U#wfNjyBnz)X zKmEp>1@+kn{={yuUKZ3kZ!4&k^_7f=!C41A@K$o(UmnvBC$XP!6Bic&hADyb6Wj~J z>BHX^2f@KJ)~F-kCjaq=U-7i#LK|E5mF&YKHZ$pR*3?kQGL{qyoM6FCofi`qcP)cA zZIOG7%twJ!(-cAtg5;w}<5bE;2&3_ci(@DDO3`3PMfF#R(8OT>;_gTl{Z&H;!cKzX zY{I+I>K`eHU*ddF5ga37f@`dZBMB!x^=1k=4rU5OFusl-LQW4f-L&)Jk*QBNqin&)!8-6W1M1OG zscZLgSi*YEv-2*lBGUxg8WUPsuKxUJZwO1f%VJO{whi~}4qPrI z3@a&?C5PQuX_Am*4kU~S)#!&9kb_IkZ%Q*T9cBiNH@MIhxe#hkxkHXEf(sAKQpM5J zxan}948c|usYDnwGIUTX(Rn+u#rcV3{fH`uMD35ksfQIO=irFIBbrEmJy&mO~ZL>!w8a; zAf6an32WW=ZoI8DYSwDLX-|t4%%vHgCt7+)Rd{S>gd&GU310rJFeh(}+-|rmM!v=@ z(mZ(czS!gfUs!2z<(G`_*}FK7iTX5@yXbWMN=;j2vv_E`X(#c6Z(jCOzUwOr5{W5A zsNF9m(M&jbiY)Yoksh)WdsoWM)%(1PQQ^!8fkeW#0Nqe%tSAyK=wcNjAQOK?$mj?C zGAwJM)-<0zi;QT=og3F6zQ_cUUJrryj4aw7APPaApjX_52Rh$x&PW?btg&%Z=IEuDrerr)l|h_QNle)2Q6~%uy@u<> z%m<^oui_QJ81-?-x(F=Q#)XI$>Lq$YNLJz6%j<%B;p{gdEl24>5v4rr5n58`;g%nl z7>0N!>GsPUYv!6dnS9PWZCl+fEKEm52rvv^i84VViOVZ4>C$|HQ$v#Bv^|q08p^w9 z;vb-5(TnzR^B}CO_}_ABJgjMWg}fL+RKB1SILSS=38%JWj+T5~3WR1W1Yz|rA-nU8 z-!+Fpta^u~xhatIPZgXlEb*jj*}`BQF50yP{TMC|h1b}W;Z^`#l%)nA#_^u~KN#~VV9KRyVo#6haLZ^EdS8QV-b6uLLZ57zuNV+h})t&KL3i&qH}_u?B{yL zdM5x<`$M4__6kS>^9(h5uft-s_XDa@^q9%e38Gz>Y}Lo4mnh>;JGD^QF-+qQ7)1vX zyf|O~H`6Qq4KlJE3&WR!5QcXdiSZUYWJA4H^t-%$K>9fOdWg2^#c1C!yhsLouD^C* zggj;~kQMey7@3j&Tet&0MxC0glB9V;&FWDRVg<9aCt3vbm~buBOxskTExfK*VeaAK z!Sj4A?fE_w1skxkwYQo9mk}_=b)MYJ0QYGNW+8XSx7ri<_wur_(`LEe$lu>z&-lMC z=W>?A|6&e4J%E;}LiO&W;KqgB?LYjz^QVia=^21yxBf8$BttD7`-|9L0P~wZt+-S6 z>hizNveqRl+}|Rp$R6;e+ z$2-xhLHs?h=-%&VZvkfv=_cZs9uxp*Al)}R+2~u~T;zLTqrW=<@I$~MxhLfJA3&w; zzTOB2jLCpg!%5?|egz#m_O9 zxBAV_Hs|eb^i16D`W;K4bCzbw^W}APbZ&cwfbL~&r(PP!53uxC+B!~BY0zI^Z}~vr zji#428CK-X&-HiG<{iG1$iqpAmb|u(PFglgKvE=?@B92Gk8gJZOVvI7QorjAc|l*Z zdkox{PqR(Lezwa}-IOckus&uh=Zmn6RvmpO($`si#K6XmB&|K;*T@x5b7rtvto~`W zu&Q4o{C?}Q@q~BHTDQNSm5tleB4i-p=i#+StG9=H zU=GA;EImK_pKJr1G1VjK@Dn)-fS-i4wn+(PwMkANgdXS#jx!CB) ze#(|SIPSGu+Vf)hjD94EB_M#D86STxo9SE{#Ov7wMKgCt>aq$otBXTH|Qf*gzH-)6* z@syF|0iDw^+HG)7tT2k7R@fF6IZTKdM&$B)GOGff5#$6v8P07wJd#uQm*Nk&C)#EL zr3F?6oVRL~1YVyXjHLhwhC;({fT(MBF#FZ8wO(?*t3~hrb@qvn-Ibh*DxFOcK#zEp zKfgXEjBEpX9+)~UO@G8YpL~t?!Qa&1%u&%a0Mp)L4dXcRpIW4Q<z?*6qJO0mWdCdqGz^V6J znE0p|=O#MJuZEc9CR+T9LjRIl*Uh7X`9G74w>GNHGF!6arQ+-c>@+t}DVJT9M+Puyy^>i_&aNe%NjosKcm@99{D@Ju{(0c;O| zF|^zztidc$aF@bj*lpbIeyjq}j)Be7KUQZZUzG6U4G`&oc&1jt^#n|W*N=VwvQu_r zoprySeFVHU6JHmlyU%1<%rJN^Nx$dYWi}049)z~>R(B-7w60H?E+&Uf!`EnwiCe4{ zFZJh+4}kH%bcEeynT_auNOuGzT+m3p>FoHUvjq&H;o)zsLt&9$lYG0wN%V5ngAIF5 z$*m8pE!)h4<>PIFt!@#Ya_?!@gWWx?zLXzH*q&^yB%fXKbXO(9!E$o&xr z+IhlW<9>#&9-iKwKevqfvM5Bn8_Dhu@mIS2zoO0Pn2^l$_1!#;Bawip0ME~{E8`2=d_U} zW-naNfvbV%0O25dGb^=1}r5$Tq6KXL&*p; zzVDa&W!*S~sVL_6fkubI_z7^P&QvERsL{OAN|_||(B!w|&-T6NN)CE0!%HoNePt;W z=j~CWAVQ431ka#i?BUq^yT7pA@;l^2@p#aXw+dqQpeJft7~kll1cD&lEGD|yJUI`;K> z{<_e8$=8ATfz4~1YRg;M82wEgdO@MOL?vI$)X%&JVma!mlm|&NM0ydYU8$i&dhs>I zKuw_$-Dt&?6}OJrGI2@5U{tm4WK>9)@0?65ERwv3u3OR>XV)E(yGlKpVEfl#E*c%~ zPI7o2%D;Bf1l{t<^C`Tjh&YIY^{)*8K!N`i zluBTbce*BGnNvLz#vmWf1D927pGyPkWu8SSl^#@snh#aRP7)R-58`oKI!^8C4014- zg`V}sERsbm%AAa2@u)W8r}@D}NK%U8+DxZ>#$N&2LurEiLp77}%5VqfdQ3x-ihv0N8tU7Hao&Ap6HC=!t1 zD!`c`x_ngS)h0OgkxnekLXC!Jd14&1y>oowMnefnmEgjyWs_Q0`w;||X?I;Df9y$a zz}XH;3{ve3tZj%sLxu~lMK>R?2@OJ}p+OHhF>CjKoSMSmHt_uVd{hpLw#CL3y}=RX zvLul*q>sTB$+81+)dyV`Q-(!FrKZYB&y0}Jo3YPCfeqh>4W`7QGvG1h(vW^n1Cx5F zq@X^57ZGvI&G)W~i#xAPv1Ek2;x0t#CZy?_FrBF&b9D&xPzw{Cx_}s#&w-A^8Xv|KUFVh;AiLAhdM}vG>fd>x`o0Gqkxa1_~m1XVXvN^5oWYO6O{<>oiK!TS@>O%!4n70nTb_d^0sG*#q$^n1b`98LJ=Anu=p zG(Pf6=3xxOU!o-aSUxmc&@$4*?4Tqkufx7l!Vxn?K7l_(K+X@%bOxD|8LHUQy=SB& z<)yFYjKCjrXueQ~Aso3*qj68lAi?P5KvJdkO%{qFOP6Ml+7&6BRCw62SKw87(8)?p zr|D~hZ2z;8iI=74%^~$2T%u8W?5Lp5S$SzN7&|(VxtI*pIVyuCf-M>p)ccDDbOQolJQDE z5Hr_>s3f5g_lh>j)LUunZ=ZR+tSbF|c%f1%h=z2(LM)=D+N8ld#NKPVz8uCSE{`eg z(e$iGrnlTiYU>QSKIbZ5?6|A3@|OUr+m?NL4P`0#&0W=p{abE6qqI=+Dh}(8u0imD z)VJSUWe+SnMux#dKk552-&^?-DZ7-1xlMTZr9PF5E?TZk5teE$saSsp32xOXzCGUu ziIUyH$8?1-;}ZMdl3?NweH{`(5$_d`6QvdR{*AuiWSnd8tN0B)4JxWADx|bpY8eTK zmIfm+Bq$NJPpTBeLK7p92wx0hxkRo zlkRM)`QhURq8RE$MVk55d7_NlZO|7x4fg$eeL6SX@sr;7&Z(*G4E^&+* zSaG~}@3`v6<{%0W9>K#g^ZfWOaXDiXHx(^Zw}Kl5g{O%?qTa#u01QfE5<#UyO~wQl z(nP^n#ES?n!h$Ms1_xwl6V340R6nG}RSKd?{uYjUvlz-iNi-7?>?sP&Qc?&y zlAf&2tbythiCSsxVx%kHt9=_a8jekxVhaAk$Q_@t=OF~t87R2MTu6#*T4Ce|1Z=At zUUkC!0dw=mij@|64BUXLB_g$w3gb6*AzS3J#hsJsq)}Ir<`E|4u-!yr+|l1;QBNP^ zkdun(OT<10MWTizMoWsUkV_FFs(HQ*S~*Q?$4ps)lc)m!nY_{{3kng(P{PGcgGYxF zh>H*rz9W<_8K;B)0u>T{OrpT1 zV*sIUv(ka5br{-XvaWrn^}fL5ZQ1#2W7yu&p}dLa!<}kZ*mwl_U!g?RC?xxI6P)_D z6eQCR)jq#Zd{w5OCMX>5susiC8DqZVjYz)q>P^-$36}7XhmKmYC~7H)kS38C>Si<+ zq@li#+uaDZY79S9FP?v}N05mG^7xV)N`0kcbFCr}d?GwQ#$oU=FNht!6__7Bu*&Ce zVX+!dm%^UFV?T>%u~*3%uXAIQuW6B!RhuO-{(aTyHpYOpi$h;Ok34QzRpd}QX1*S~ zFvJ}Hef-oat}zZfHk0^g*8M{E5k6qKFt(H zRH_O(OVj}>!Xa}Wjs_b{HE)AUncvp}5gbL4-Hw>Mx2G0|lC^hfu;CiE{D}@>&}?=sL+{M?)wOf+OD;@!iQ;4&f8NBgivK;%eR2V^W<~hv}46LI$ z#E?!v{4!$ZoZKi2fwqIvpb&68abojq*2}PD@xBSKwr=Tx9!9gOt0~2HK3)ar5KCB{ zvNCl#UQ3v)Oj7L|13l*0j|i}=l89~$+$_Cr;-)E5HO)IZ)t~URk-M|R;*&k8wT$pk zNf4T8RGGocU}=I3cY~KxqxZf#Qaj72^cVa*-qxvBdRaAEZDk%G^)Obj=J|B7O^*24 zXeo$-51%O|JdyF|j?bJ`aVW=Zj5A!a>Dgzr7D}$K7v@d4#q%319ZgFY^@{mgYwk(zw>V?NGF2yv}iytgA$}mg^-S?E0G+e=YD>bMo6CQH^raNX8Th3K^Oo z>*s!_$608&)!=JnmqKw*JrUEtD7*Tj@Swn*%KHR|O$v-Mm+l-)GBnrx>~V&;>&Rqu=W(rxjIf`&*hA-EvAqPae+cobpPa=4XQ9)F<&? zRef`3V$o83p_n6ye>)JgNNYUWZSG=p1Z*T+Tcrgv^Ddav&0qVP%QRQ1O?f1@KkGHW z8`DJZ6JXiag63AW5aHW3#0KKE<0bm+)3Btc2`wtCKNCEPMyXqvs_sVe;)`gQ$~qR3 zQIc_ZaY?COtAk{-$5HcfU;kF}!T;m0IJFJkQwTCX3Oy}H4{6;ridi6siT@_Ry(ajX@Ew?)ibyr3|(b(bE#Js-b; zdZ`eS8ziIQ9ym1nz+dB}RMKZ4kSK6w$1M_AjjIW-Vd_mF%rCA?YOYc`Yc3es5$T_4 zu>LV0#+3S76M0cd9#Jo|UY2fT2- zRcQ>2%~iKwNJo|&7j>9#i__thnlvL;WvH&>Kx$AcBHh^~6Pan4 zPB_E7SN5&UwGG2ll3RRAcE7SkHoK}7X^f;?|I`uuN*{d(58~>tvpWotwE#3$rcb{b z{%rPNK%@1#jX!Kn8?C2a3mvqRg^`t{S2u1O8FK6I6k#yLo*96jRSiRBQ{T$wUUwvT z^`|9j6E1|m3ZJS4X`D4}D!6ty*6!p7U+48}qs56;1e?_HuIBN@w35$0O1Bv<_ zT2P+!?@aB#NKVs?-40T(-!+oh^fym&HBaI3*w2iK=&ot6&5_y{pU$v)fZUd9yB9?A z|4(B0k3_{`gOR)F8l)d5dja}}VKmk@LU`;3nh-D<*yi=S)^@b4+8k9Ub95TP)(c); zA!>nVQ!SbEi@c6voM2m5$1$(DmOP?^R#s-?)x!T1vD$+E)4rh9l@$_GK*lgU8sTyE zePOXcD*MRoq8#gJrazUUs~W4u_+1LnaRoe!{`;5zY0bY!1Ik#ftem|M;QhJ1l|(Ai?h7dKW9v9q~oXI8H$*4isho@Bf7V-`oG)`kw&j z_>xmYADP8DoGbd4_b6A0ehMgW}P)#_DiG34VlXxlm+@`db z6%y^-il#}4w?yv8kB<0(O0C_MM*~(T!F4aCTYGYGZ0mcH*>+DIns)>569@%IFoQCU z+Ww62EbXc{xMSp=K1J2`P+5I7P}_PW9s5l!(6%O#4Ns-z%43kP{;I5|Z>#3oI%PeU z=J#CcuR5S~@H7Z&LR-uXfR;*h<2>`gw`!LwNC@p^hvHI4r)!>B?rU&gp3$GLeTVK=ID~ zq2v^0)_w3uVX}sk{G38#O_ddsAQ9(lBDR~n9@>yR(mo3(;TiCyx+6-hUw)>W=xxC zt)(V>p7MA+HRM`U?#p0BZe@X<|AJ;3Dz`jq$F?{{g;?^U_BWeI-ijCzzDeyad!@TP z%OV48?}a8AY0}wA5Aw?<<7dlg*e0pK&`%~h0~d*`3xCT;&gd?yHvJNVBf2s-<3duJ zb$C;`K}Bg&euL1(nnA0EBL?XNxzxRe6?*L9%(~%|as|b-YXiS*NH#V*=H1#yO^bqG zqYlbm9ThF1sbT4v*0nDYxgpjy=y8je8O;2oDeXCJZxr+J90)D#^Rs&=4Id>haN|)^ zdKCrjSq=_x7%+$yW%TU%@3podv0?bCt;;w#**oT1pWg@w*#F3pY>)|X+*d{WkoqlE zU`R;W=&x$~M*gq)3PZJSU)faS9?4WUVlW?C#a1Ib7EbeoHte?{ZCCn4vikSp=w4jS z3VFYdyzUp$$_jsu(1zXj^(@t#K;>mkgD~C46-`Ry+0xA0j5=97#p%>|dFO2OGOJ7$ zelAP1Z1rYHzVMu-_tqUe57Zq4qSCc4y;8fVxU_r!ZESBsn-8rVda9JRef`oL%EEWz zK^T3n$ETQ{q%UVlf%aL(CCy)U)=)MQ?&b}F31UUVie376lWlRHF?nje$iKpw?}iOu zKKeHYh% z2QQmHX4Wafpd@YPli=p1L<2&j;_(xu=m3_QzqxoIa@Ow^$lW`4HCh!qRFm;{l}U|f zkXSXFL~5bDU(C0)(;`qb!IhP2)ELKlS1ez7V)l&3)0+a8$BvNWTKR9|GPRSsi0y{V zPM8$q-?^g}`EZ#ER_$i$s=xd>X?0Cz?3wO(URxn3>v;pF6w0MUL#2>m){Ye6$5W;z zPbMWY0e5g~L#*>xS;0VtLsabvlwMZZ_f}5?C;?39Sc0YTLi3e9cIHx^GEYW&i9@?a zNc}-4oJ*T`R<_kLhQ97xb<-9!k*WEF;Qoiw!ND`W_>yH%?45B5pV9cnyW8q)}nye)y!S_e;-~)8x zab_N2MjnpTspl@K{>r+pJ%&A??TM4fBzw~EozQhS5bpNBDax?%&Bn8-R4m7#$|Egk z8OqxwLJP}V7A|kVP{UTsHq8=v(tM=u(dlvE@83qdMs>ym z!$q5DXIy1B;AZE;a1~+SGvolbnb*po8KO>Uf2Sbo;16C*gq(i!K9v&+;+5Xd zur_w{s+XY^rpKlSuE?xd>Y9JEBq{Z^NpfSHEz^A|<4+cf9MX1AXJOzvM@P^&X%(LF z{NwRwW;#LdplXaxro6RSapFP6^3%?gsV?X2B0V}!IoolygDWh`h&do1C3v;EPm2tl z(alDqKUO&*I)zODyUUltUYT?@*D1%jUP<**-Zc_PQljnG2j@~SM>l?1STvhWKv0U= zv~zG5$uw(wd-!EiLSLh?v~gLs;m*nO^_bdx?-v36R-MY`3P@%*7;#l*C2mC?^D~F7 zjaKB)dIbblJUl4G+SYNP?RJe<*7mAFyti<vv)wnZgja-@{BFN$UNVBow&x)65dx=6C&Mdhx(p>fE9Jw=!A+m#J&OT#pi zu8FiYbtaiGnFNLUUUlTK+c0g^^4x{wJ!93fl^B?^8{Qwf#~BBAc#hQXP+MZ-S+;`O zXtrjD@smEMG!vfz6#CZM=thPdhKh0rjL^%nv}=skk5Z|&E8K0#4qb4(;|{kjxf(TT zc}aHAve$a88SJN=21(&`J=acR-#%D4@>(5r9YL7-8k?$V$*x?U-d%You?}U+SWhZF$?=0l7s^s#}AjB^P6fUa^5cMNGcsRAG{iTeHq;Xo6@_D#!m<}7?Dr5 z7q4+>PN+OSTK)hVK$J~ALneHIO7s^YT{*=m290UPdK=cSy3(WJtj!hZaviDxRf#qr z8+F8@Kf9Q027(cqzMww*i8g(2kP<~HUSg6W^*-&-Yx~B;3PzX~`CU0)JtkU82}**J zH|0(8hMke)U=f>uP&t28!Mmzvv|FLTs)QEYhqEj0uvf$^5+1oV$ses>4e#;DS65?G z(q4)LwE4bovdVf_W3N+mLqrF_2224GO=Hu~A-9gWR=~ zf6)-u-86fK@0X%+Rn|^~Q9*hDKgNr}R8&uZ7n+?%MAb@kpKZ~5?IJX8!dA`*1J*PZ z^A2L{CS^C8k(dZuKD)Z`1;#F@l#NCJ?b&FOB9FQqUvFmRj=7*yud+01lQ5+j2bYqp zxe$`%l2Kbc9C;O+=*1zJ$l!T@mwh9o<0TPEP_&anrD5v~T3rra|7iMXNiUOw86lBx zTl!^T?lI_xnt~mMX;xR^I9C-eXH&_fwpZ9DUkbZBdGD7Gy5oj{B>@wL>$HwvlHXEi zvY*#3lISYE*CS2ev{kF1J*_)XBgg+}B_W`pYc15VU}J5QU3X@g?x&p0!P)j9s7 za>|iet|6&aphe`aW`7kD^o5>7qo%)>2c!5ZS70G3s5iuQurSAR^((SFJOWeW$`Pxe zXDuXkypGTaRI&b}c)5@ywF*)=DVv;5zCgsF5yKGPOra(CN*vQmUwSD0M5nqUE_csz z8eZPf9h@0&n&GbJ&^l$3Rx|2Whnk*HFS>`+{0(X#IXUMtVpy}Bkz#4ZNY21h_jYxZ zaY$a1qGKwXY#ou5Lg~$DKfBodUY3UZZlU_$5yB7D?#99X+sj1C^+UIliv^nGfKo? z(v(N$u_&kwaNCpTgE`zLTd^MNtlz9MbP#E0AJIc{_vVrG9trn|S@|iLx6jn+jj%#j?QlRESxU#HWxNxGRVae#63xF` zTNcP0uxndm(nun}T$Jui3NR5z?gvvL5VFva6?)Sv>)CQl)}8WcDoZQt8r&I{I#}*( zdYU05?;P}izT<17uuf+gP<$j1kpg3Al;?>eJG#e7t>#}O$iO)u8Fk~9I)g7xmsAjF zv*RwGMEO$0Klf&iVV&H{38jH3Ajss>($ENF;yjOm1=-)DNjKtfg`~8ctn&2>G<;Rw zW4a+Hz7D-|0r*xD1!i1Q1<<R&Z{m9j*}Q5Q0z5!_)Yi_S+eFs%KRB$KyPN4>~H-ih>sm^M?{s)eGM(qFq literal 0 HcmV?d00001 diff --git a/Resources/Icon-72.png b/Resources/Icon-72.png new file mode 100644 index 0000000000000000000000000000000000000000..5b1ce47ad2cdad2bf3829b279ae371bdd1d86d78 GIT binary patch literal 11414 zcmV;HENRn;P)Oz@Z0f2-7z;ux~O9+4z06=<WDR*FRcSTFz- zW=q650N5=6FiBTtNC2?60Km==3$g$R3;-}uh=nNt1bYBr$Ri_o0EC$U6h`t_Jn<{8 z5a%iY0C<_QJh>z}MS)ugEpZ1|S1ukX&Pf+56gFW3VVXcL!g-k)GJ!M?;PcD?0HBc- z5#WRK{dmp}uFlRjj{U%*%WZ25jX z{P*?XzTzZ-GF^d31o+^>%=Ap99M6&ogks$0k4OBs3;+Bb(;~!4V!2o<6ys46agIcq zjPo+3B8fthDa9qy|77CdEc*jK-!%ZRYCZvbku9iQV*~a}ClFY4z~c7+0P?$U!PF=S z1Au6Q;m>#f??3%Vpd|o+W=WE9003S@Bra6Svp>fO002awfhw>;8}z{#EWidF!3EsG z3;bXU&9EIRU@z1_9W=mEXoiz;4lcq~xDGvV5BgyU zp1~-*fe8db$Osc*A=-!mVv1NJjtCc-h4>-CNCXm#Bp}I%6j35eku^v$Qi@a{RY)E3 zJ#qp$hg?Rwkvqr$GJ^buyhkyVfwECO)C{#lxu`c9ghrwZ&}4KmnvWKso6vH!8a<3Q zq36)6Xb;+tK10Vaz~~qUGsJ8#F2=(`u{bOVlVi)VBCHIn#u~6ztOL7=^<&SmcLWlF zMZgI*1b0FpVIDz9SWH+>*hr`#93(Um+6gxa1B6k+CnA%mOSC4s5&6UzVlpv@SV$}* z))J2sFA#f(L&P^E5{W}HC%KRUNwK6<(h|}}(r!{C=`5+6G)NjFlgZj-YqAG9lq?`C z$c5yc>d>VnA`E_*3F2Qp##d8RZb=H01_mm@+|Cqnc9PsG(F5HIG_C zt)aG3uTh7n6Et<2In9F>NlT@zqLtGcXcuVrX|L#Xx)I%#9!{6gSJKPrN9dR61N3(c z4Tcqi$B1Vr8Jidf7-t!G7_XR2rWwr)$3XQ?}=hpK0&Z&W{| zep&sA23f;Q!%st`QJ}G3cbou<7-yIK2z4nfCCCtN2-XOGSWo##{8Q{ATurxr~;I`ytDs%xbip}RzP zziy}Qn4Z2~fSycmr`~zJ=lUFdFa1>gZThG6M+{g7vkW8#+YHVaJjFF}Z#*3@$J_By zLtVo_L#1JrVVB{Ak-5=4qt!-@Mh}c>#$4kh<88)m#-k<%CLtzEP3leVno>={htGUuD;o7bD)w_sX$S}eAxwzy?UvgBH(S?;#HZiQMoS*2K2 zT3xe7t(~nU*1N5{rxB;QPLocnp4Ml>u<^FZwyC!nu;thW+pe~4wtZn|Vi#w(#jeBd zlf9FDx_yoPJqHbk*$%56S{;6Kv~mM9!g3B(KJ}#RZ#@)!hR|78Dq|Iq-afF%KE1Brn_fm;Im z_u$xr8UFki1L{Ox>G0o)(&RAZ;=|I=wN2l97;cLaHH6leTB-XXa*h%dBOEvi`+x zi?=Txl?TadvyiL>SuF~-LZ;|cS}4~l2eM~nS7yJ>iOM;atDY;(?aZ^v+mJV$@1Ote z62cPUlD4IWOIIx&SmwQ~YB{nzae3Pc;}r!fhE@iwJh+OsDs9zItL;~pu715HdQEGA zUct(O!LkCy1<%NCg+}G`0PgpNm-?d@-hMgNe6^V+j6x$b<6@S<$+<4_1hi}Ti zncS4LsjI}fWY1>OX6feMEuLErma3QLmkw?X+1j)X-&VBk_4Y;EFPF_I+q;9dL%E~B zJh;4Nr^(LEJ3myURP{Rblsw%57T)g973R8o)DE9*xN#~;4_o$q%o z4K@u`jhx2fBXC4{U8Qn{*%*B$Ge=nny$HAYq{=vy|sI0 z_vss+H_qMky?OB#|JK!>IX&II^LlUh#rO5!7TtbwC;iULyV-Xq?ybB}ykGP{?LpZ? z-G|jbTmIbG@7#ZCz;~eY(cDM(28Dyq{*m>M4?_iynUBkc4TkHUI6gT!;y-fz>HMcd z&t%Ugo)`Y2{>!cx7B7DI)$7;J(U{Spm-3gBzioV_{p!H$8L!*M!p0uH$#^p{Ui4P` z?ZJ24cOCDe-w#jZd?0@)|7iKK^;6KN`;!@ylm7$*nDhK&GcDTy000JJOGiWi{{a60 z|De66lK=n!32;bRa{vGf6951U69E94oEQKA00(qQO+^RV3K9=87&?)eMgRaH07*na zRCwCWe0!K3Rh92=?Y*ncqfbAQekJL=Lm&_m1OX92NEmdCgYQuuKDaZ=)lbI(XGTQ7 zd+&_;jpN)wxavh^e8FV`%7|A5eDD^OH-dyf64F4D?oM}-?)3AVQ&oGd`^Vl@r%tC6 zGOzDWLv@`>@3U*KRco*HTfe<_5pqAnH*5eAq34750sw}-cg|vM%!Nei&7|l=faVjC z1`&t|%|ElR|9}5T^OXo70uix*j0(#Z5&or3l3N!3mk0I$z{rgoAtLDg(0y}DHGJbn z>iyuoGW`8>7m(JQmC_qXDUdR7=TtG||0jURk466T=tz)_5h4&sHbxPFut1n0oIset zZ0{3mFYdkc{=KOV4pGp^P3Ke3`|pzB8#cU88GQ>W18stYl+vU$5jh1ltcMyM{3{eg zioh)Yu{xE!9ZAEASRld>wj#{dCb1Dtz=^%E_e1ynAk_r`(C`f#03v5OH*EMTWz64e z6EK-TDWyee15%1eDIrqcXi^0U01{B9COM3{BzqtNNi0)h86ERVsLjObwXvrS^fN%HC558d}ahi}|S5D^-=aU*&@crTpgm|JuZ zLg_%1F{HGCQW~TcNGSlyDTi_<{4cZt5kaA?h|@ptL%jX0W%%63&HzuBi< zA|e41<)@cF{fnQxX)Dr~zWE3~`>jW@d_gOIe)U_iW=)SolQk%JgMS|jnr)knX(l29 zW=pVEm=m%|0*+(hB!W#M;y78^M=jhwG@lx8x7*bl3 z&b86T{X3a}NEwjUn*o4^zJJb=ASmq8VIff_&^ic7n?RH{K-#g=0LZ%D+O zC$Y6r-NaFSiPG9!AJLq!dUi z=W~>zR5?UMAO+=pkUvP=2l#^|cTPWJjy3`!D8gIMT<)GOY5J4bbz@%9dHsw2`!Dd( z|8^&#QUKES|Jr-a8ng1UxN9$?Qj`luY+89;2sulY z)JDCv%IsL-@rM6<)&?%-!gqrwUKjxY862-kS9?1)u4=*RP9p&DtsifZGcUdg2y{A~ zMMRGJUO5QlzL&P~vN)YPbn74tc`H-W&;^~S0GdyVK#GXSg>oOuiO7K*0!W-(|Jzcc2L(wS<0|Gz~ZyCZd zZ~qPuXo5-Y_vr3**ft_g77ySkO$UfPj8V?a6ak=liiiwlX4$Y*?~%oacdz}1OBDZ~ zOEkPmkOTH}wGn|(!C?P^Y3V%g8+dBlNRwwB7_Uhw2&AuTJ~~T~H!drH2-rC=j;`~r zgTxk4F6a^#Ak>77D~bp;Fg_ij1!3RF|6X>?0SKzCR=P+YFEGVZ& z1C_fX9A=_5yj0%lWf09bmaGjC0WBd;{m{)cJ{=Je@L%7s3`8In0Sl>SuwwaC}9wh z_#cRfbH462!opbG*#Q;@Hxeu8EGeAW69@qO<<}mRC!X95G8!TxwR$48dV>5|k2K~( z3@tA^AveVitHl%*;HZEZ5fPO`dhFqsam~$70Ra8ci9OU+3CW6NTnQ18Wl5(}_HWw< zN4$e|T`>z(%)}g7l;*i`MKlaanRv<<6+bu(M{ZF2E}smZp15oo2- zwy-Q%}!J9nTCB1^Os~;*ojGqp~168lb2M&pf$D zZn*P#0Kj`rUg#GUq&a1o)sKikk;1--DOt6kTTWcohl!b*#MUD9OO_dn=C>oUR-DuX z5s{7}VbweX066_mZo%NbDFBe~{Omb#N$#ry#eX*oIx}aEMAtcL;RSUbR8pT<-7ZJ3TiwKRObR0xP^+d3y+n}Ib zKi1=fYi@Ze1Ct_ht{PM1C(myaR0OU<;x=?ddD@`0l zkrMA%nxX^|Nno_Jx0J-P^HmBYm0}_DRYVY@m9&-$O=BS_=ORwcc-jcc#~r2rrQ2e%q|QePW7Tfz*$i3qe(n4GE2 z$_?c3bNZ0KIcMF|(T0S%=`WTA3o9CJ1=n|H$vyMye)M}F&=78nHyv}1A8M90Yu6Zw z$|;3%9r=vdU1vOypB|)qh7KVr89aFZi`Y3ZP5^Mu>Q1W0Hd|6uO3{JIX}q#;)Mdt! z$8p&-aMTs0AfNn{Zf;X91SlAT%;9?sn=o)fOd)8Q*n*~m@5cpf`m83h><77-Z2=fC$D0LTdoDrhg6jENNy ziIPNK9yyR&g*cRxIi#Ee&y8b_HULBsp|w=-u{*_s0E?ibsAO$-AT0r*WC)f;9(rOo zffL!nLF@fRs|%=X@LYRZvT@K+-V36nP?Hp@prQ ztbjVp4n%(U_N|0UNHoiWnq|slMb4Ts=2SJYxM&Ou;7oIpv#?^Quw(Z)zV_`$5iV)R zSAY170FbUqi1{sn5MUj%3 zdL%h(6#%kp->7JhZe=f;-ZglAC6E*(GbE8ityh4!h5da;(PRSfo-iDa4mb zLCafC=s{c2fIZQ2BoOG9SPpF?0tt1S2P-pJHhkV@aEt%%t*8I1d`Z4OeI_+FKlyk6CCYvRJpM)pskt zs52d9!-t9pj3Ou^u+N`T>i`^2-QH3Hdy*_&L`bDo3RE--aWQ~6zS{Q(#HpOKmM;7Z zgJocPCUWC$q$tg~7n*X=986-Eri8ZX3k+wM+06wOv~a#7sp<)PS1u`Eltx*~hwwlX z1SG=~bsVfER5Dr?wil>i6s!P=df)e}J2Kg9fEhxp<}rbFcShe2~#_onAbLSf1o0wG~6?`?5D zD6(+8a>GjU{I6C77T}KOM)35WiP@YlmUI+x{_)*d)Luj_wuf-y1VS-T`1x}qcw*N$ z0HCL{LMI=y4Au&sd9I)O$7<*wvp8dMi1{Ujgavt`J3U#p066ckC`T_+v+Udi7L9{S z3>MdcfB{kd3~}k2q~su}lFlAMY#E_eLMrpaLN3|IWX#9@~lO$cjhd#3E7-HEn);5IgrzW6hd1_{mRxjFrn4 z$V=Owz$-hRB&|ca@1ft(7yjY@;E_EM&R85`UP(D$=tRvd$T)w6S+Kz^c?gSuJ$vop zval-?&MYVkFYHZ`9clsRnKX~u7{@JX!?xWMI4~WducJiLSb3zBqxr^zuRtKU>WYi; z<_)J25%8MVzYA@hO9=q>?Ae3UPd^@xh5gvMe;S)MZNeRQ z+>t%`)$9HMy-QERgLnQToqz7BIC0Giyzj%G$0K_pylGX@`*wo0jkUf~NZAz2TaZIa zUO9~@mT`kF7i*Z44CO`Pr5c2n3(|i}%Z0 z&U>AVj*iO7XI~<1ol6CP%*@Ql;>C+GI5>#DMT_Lom&c@k|BR${8wY1%dE})r>FMrH zl_NLbe6zgc9q*8b9(qXH+Pme9^WHCGV`I|Q*^bX%aR~s(j?qK{?Zy(b>&s}8N?Cga zcp0*^fOD1QoY#Zo_#m0Xo6D^Em;+Z3oU*zD0Pxa*8H7dwjmj?^tR~ntJcX0juf|(8 zo{j^fqga3XCbYJ9WAo_~EU$`XwR8 zd*AzB+;!JoIQ#6gannsVp|fWx7A!sv)oKl!&VL=|byV>3Sc1eS=JmvlsSqesx{9EJ zg02|EaRN14HgL1-`D8WMh_bMrf~AIX8h|QFaOMe}06_aEBdSI=V**31@cRMhkS~1a z2Gr^i6-yObb^I9^7#P4Mms~>3j4Q6VB6G;|=FOw4uDXh<^%z@+r=W=F*`X5#L|Tp-rk93W;dd**{Bf1R6R9zwVn;NyIj&1Z+T3~DtverYQK zpj~5C(&QOG2u;>vDiuPMiXp7ERB7uZ0CeV=XJ#zm(9jTV+O!F~ckc!O>gnmBWy_b- zz<8CW>Ing$Q%*jG0ASOmO<1~gY11V z1IWHtw+bQa)~%DzeeQFa6)jq{2%9%=&f4{KcguLq3i|-u+SZ!=t-rq?U;5IQ@R5&v zMDDrg9=Y_=OJUt2B5V?a07OyjQw);CLSh!I>kXu>;PBX+Pwm0+YdT;ZXcWqFwN7S6T_rJ-JgNg%Fd1wx#XNwLM-&gcaIjLpO}G*Kh%Hv+Yl0#s`eYV`Km!2v_xCrJjkLyRKJyt|fBp5e zapOi35h$goR-Hmrn}*gJ{kw)7M9?4xXyb|kI*LRSb=UtlKDi#K#hwsj;*`1JOBCA)gZmhTYpZy08NXd|NyIfChyY05aFPr%V$CtJ}3KJGlDiv_&uO0;e zw1g^4zn>b61pwZE-eRm+)rKUpDeWqh`k6E_&B>lG>QPNo3$rrop(QqATWsPGuXZJ=^Payjnu@K>h0aT7du~kkrvM@QESm) zVS9<5dg^x=8XB5ih?w=0BoXa;@pmW|i+Jp*7cew@0DWy51?@IQzH-oFI(GNE?$1sH zPu7vAG^7!!s7dC$E-)8R%xHkroAYcQ7Jwj0ti1WuF6r(F1%Ujve@se&mX2~Ly{#ec zdEhDR9T}CDN~=8o^u3bAQN}ptc@CwNTzB1d0zlR*XqD;6${SX82mtX>F5{xpGZD#? z5B^AmtrWumfBTi206^As8HriMGGxn0ECA#ymmM#yZ3fJ<82Lt|ZqOU4>`g#X<_1U& z8ieOO+#;Fa>W{1e06e&J6xEm!TgKbhF8~01{I9Ns(F%$P_uuhNu;Ztj7{^y$c?Dni z!WU2s4c0AcMQjDf^;EEAUJ19|b{qcsum8GH$?VThJorP5j`XA4QpSfbzZ&~TCvbds zfPyB3n()|wgB~X?ui%OcS0bK?4=YErtcU7CfBJ%+|Dm)dr4*5NoDF5WKGIzYbJz04 z5z{H_=i{G#Ie^irm`0`|tnX{1O2MFLG`8#+qdR`}2%UAtI&{r%!3)2Ckjj+~w03k8 z5#iZqpT%*<9Y+A@qSr1$rJ%tg!~&eOxRsvXGl{$IxepIK@Bpn_yM}Ca5L+Mrzu5J1 zKmF#h=ke!PeVvB)jbeF+#+oie0?@Vt2?i$}JMh9ko`nF7FrQy+%+{^NGp0UM5MdI_ zB+Ou&2<`jUdTYW!X`@92hLq88dsQjj>Emkz7@ZHd}~t&3p>hq>j_=dQwdQ|7=hNYaN`NkDI`)GP$&SG)8AbTyxhqgG3bAFh zghuvG*0F!8PBSq}OK50ndJ?O8TG3W+LBb5Jlnfu7z|Ot<>9wntNFfMl`{1xIn^7^a~f>bzUr8Lb|4TTwhXpz2cnHFY@8s8Vy2OA zthG8cN*e=hG?a15A+24xnxf{)>cf?pc@C!Qq6aZmwJ2zXkDk30l|tYu&2p=sj+|sX z@!~GbZ!Kfhf^JOL>ex0gjKCPITeb+>2Zk|SkD!$=AE4P!o)Bta-wfl?Jx)2Nu5QKS zUq2m5o#&`AnybjH5v0Xhuw`&!VG|3RB(PD!H2lAhx26y%Wi(9SH^3V0w_uXrID+hH z%-&L*-({>dNiAaPy67QP>yCH0;DmWt-P^X1{fzeqs4~!;j zSu6`FMaV7D=Qo{Fph;dDv$S=eOEJz~+m2s<^)w{YaaNdiggpe_fl!5U=5S>xDIF;!b5!N|%3LA{tYs>f6vl3O0~U8Wa{p&r$MC}^ zchlD0Lo_x$gV=JmwY;yh1AZA9o}{NZv0!n^X{vAoz#?P_!~#6LJIa*v{&Rcq>#v^* zI}>Ns^lrPH-vEl{>JITGE;$)9nZ@hEFDxds_iM-QH9^p;10yQXq}>z~x1qPOxh{ua z2$C|^(p+xU;C)Fe!h627Rql9V%&m+nU~RX-qBe~}U|>u@m0|(0wX%0?0{ODvoF8>0 zCXr~z%b|ZP!3+D7tj*tjXgOW^$4f9VZAI%wp@YhHHMw7kn(AAc$_j-AY%Q^6*u)Au zA4Ex<4Aan8SKn=nIZuZt0M&+2=}xW$hYE+B7Lou(*_?xl$v=DJi$JYusY@goR%v!m{oFU;aQJ zPFhi+C=rB#k}xoC_oh+MnqahoR)AKD$gRssZgZtymUoOLTP99in74@~8zo4RJE!r5;MuSYMoV1$HWtx_SEDMr^5ha2swuoXYwYo($N@%8*NG-BdttF_| z6IAQgt&XtBt&dIP-sj^cztB-s=qf8zLPDrBE1{T0>WR>JO)xxd5!r@OwG@CqT3?{^ z*Ow(qKw2x53kJoaLAhX1DrhMc42lI!rO-$iXbJ;`z$h3^&`LomSI*})y7(x@3`sb3 z&|+gtHnCUIz}3r_=)mmKfk~7xT5Cn5l|ZR%jo1pnEQl>5j+yE)qZV1I))LIr6HHeV zOjlz}Hxxtl*dmTu63Y+)3IR}$1dqIA@ys5^{wX=?mj(nH($_&aW2MHLUiTK7dg9jJ z0!Rt1C8?s6-%aTD&N$jjtOX}5Hc70F z6Jz6K2?4;+wX5z^MxX0m&*1jXgA||$3mB!{b14o!nC#0uCZu(1Uv z_Wp&J@7!Qqbl{6&X^@l^Sidn`xdJpIffA4qiBTYEf&`8wAp$gsXytZohl->cSPK30 zsz(;FRj|xrt&1vg;`S1h3xu_c6kf9gFmAm^61Un%freNHB`ip+i$OCnKp@eYpcRqU zBt{X1fkGkBC>1qIp+UK*rCcy5mjWsmv=l;3#XzADxJ`M60PRZuA>Ma@nY^PG2N;=H z_+kK{!E0Ax(d9cac+JZ9kyf{8ojPev7cf?i@#}@UNQI9O@iAAl> zs6`g_*rFaMh+;t$Gor*t<9yh%hsR8T72Zm(dBLTD7i zK%rn13ZZtE6KWJgr>rp4C>VvnXc$A@_YidYVupO(3^P0i61J9XVj-3j2@S2s&EbwK&%t}hMK>Gq7h^X|H5CB3&VB=yvvjPjqvItw}$gLH` zEU=bgS;Q%m$y(>^nFWbgkf*KFKt|-3N0obv1e}E`XhmW)!Dw$mT9Hu%?VYbXLzIhH zq$usDISKPy|G+FHmWA08TNayG63bUEx}yJa@S2qul2SJ- zt)R3@NGYw9a^TMK&S`OSwg(H+_}pC8FZuUF%&PR4j3toSq$q&7&D77TefW2GK8Jwq`Eky2GU!S3|zBfp(3@J6rD@Th5Rfp zYpUX$8Oz@Z0f2-7z;ux~O9+4z06=<WDR*FRcSTFz- zW=q650N5=6FiBTtNC2?60Km==3$g$R3;-}uh=nNt1bYBr$Ri_o0EC$U6h`t_Jn<{8 z5a%iY0C<_QJh>z}MS)ugEpZ1|S1ukX&Pf+56gFW3VVXcL!g-k)GJ!M?;PcD?0HBc- z5#WRK{dmp}uFlRjj{U%*%WZ25jX z{P*?XzTzZ-GF^d31o+^>%=Ap99M6&ogks$0k4OBs3;+Bb(;~!4V!2o<6ys46agIcq zjPo+3B8fthDa9qy|77CdEc*jK-!%ZRYCZvbku9iQV*~a}ClFY4z~c7+0P?$U!PF=S z1Au6Q;m>#f??3%Vpd|o+W=WE9003S@Bra6Svp>fO002awfhw>;8}z{#EWidF!3EsG z3;bXU&9EIRU@z1_9W=mEXoiz;4lcq~xDGvV5BgyU zp1~-*fe8db$Osc*A=-!mVv1NJjtCc-h4>-CNCXm#Bp}I%6j35eku^v$Qi@a{RY)E3 zJ#qp$hg?Rwkvqr$GJ^buyhkyVfwECO)C{#lxu`c9ghrwZ&}4KmnvWKso6vH!8a<3Q zq36)6Xb;+tK10Vaz~~qUGsJ8#F2=(`u{bOVlVi)VBCHIn#u~6ztOL7=^<&SmcLWlF zMZgI*1b0FpVIDz9SWH+>*hr`#93(Um+6gxa1B6k+CnA%mOSC4s5&6UzVlpv@SV$}* z))J2sFA#f(L&P^E5{W}HC%KRUNwK6<(h|}}(r!{C=`5+6G)NjFlgZj-YqAG9lq?`C z$c5yc>d>VnA`E_*3F2Qp##d8RZb=H01_mm@+|Cqnc9PsG(F5HIG_C zt)aG3uTh7n6Et<2In9F>NlT@zqLtGcXcuVrX|L#Xx)I%#9!{6gSJKPrN9dR61N3(c z4Tcqi$B1Vr8Jidf7-t!G7_XR2rWwr)$3XQ?}=hpK0&Z&W{| zep&sA23f;Q!%st`QJ}G3cbou<7-yIK2z4nfCCCtN2-XOGSWo##{8Q{ATurxr~;I`ytDs%xbip}RzP zziy}Qn4Z2~fSycmr`~zJ=lUFdFa1>gZThG6M+{g7vkW8#+YHVaJjFF}Z#*3@$J_By zLtVo_L#1JrVVB{Ak-5=4qt!-@Mh}c>#$4kh<88)m#-k<%CLtzEP3leVno>={htGUuD;o7bD)w_sX$S}eAxwzy?UvgBH(S?;#HZiQMoS*2K2 zT3xe7t(~nU*1N5{rxB;QPLocnp4Ml>u<^FZwyC!nu;thW+pe~4wtZn|Vi#w(#jeBd zlf9FDx_yoPJqHbk*$%56S{;6Kv~mM9!g3B(KJ}#RZ#@)!hR|78Dq|Iq-afF%KE1Brn_fm;Im z_u$xr8UFki1L{Ox>G0o)(&RAZ;=|I=wN2l97;cLaHH6leTB-XXa*h%dBOEvi`+x zi?=Txl?TadvyiL>SuF~-LZ;|cS}4~l2eM~nS7yJ>iOM;atDY;(?aZ^v+mJV$@1Ote z62cPUlD4IWOIIx&SmwQ~YB{nzae3Pc;}r!fhE@iwJh+OsDs9zItL;~pu715HdQEGA zUct(O!LkCy1<%NCg+}G`0PgpNm-?d@-hMgNe6^V+j6x$b<6@S<$+<4_1hi}Ti zncS4LsjI}fWY1>OX6feMEuLErma3QLmkw?X+1j)X-&VBk_4Y;EFPF_I+q;9dL%E~B zJh;4Nr^(LEJ3myURP{Rblsw%57T)g973R8o)DE9*xN#~;4_o$q%o z4K@u`jhx2fBXC4{U8Qn{*%*B$Ge=nny$HAYq{=vy|sI0 z_vss+H_qMky?OB#|JK!>IX&II^LlUh#rO5!7TtbwC;iULyV-Xq?ybB}ykGP{?LpZ? z-G|jbTmIbG@7#ZCz;~eY(cDM(28Dyq{*m>M4?_iynUBkc4TkHUI6gT!;y-fz>HMcd z&t%Ugo)`Y2{>!cx7B7DI)$7;J(U{Spm-3gBzioV_{p!H$8L!*M!p0uH$#^p{Ui4P` z?ZJ24cOCDe-w#jZd?0@)|7iKK^;6KN`;!@ylm7$*nDhK&GcDTy000JJOGiWi{{a60 z|De66lK=n!32;bRa{vGf6951U69E94oEQKA00(qQO+^RV3K9=91F7#}>i_^0T}ebi zRA}C%nt7BQ*LB{1_q|uu-80j(VE_yU3yF;jOo$5vQe3ovt?Xtv)+b_G` zTK6?$ql$46##peTf=F(dEI#Qi>{JD7gvc3^ETF3YAC5@N2^3YlN(i2ir39bWz0ZDU zsNkq=WTNIyO(aUE_&LPDrFa!y^Tx8xJZn(h_Q%B{=k0$=8Y97Iez)Z zA8_|i_VMmBS8(%nmvG5@*TY;B??awD`u27Kt%K5u=_@m!9${N3vWknL-$tn0O0|ILT^>{lKjnNKi1C0fMMasN9$Bfr*& z$rlx4Eyg*`ZH^FQuQFoH7Go@}#Fkcqi)nvZ9^E|aR11EkO-cf&_W>z6VXi5+|KlD7 z)L1K5y=#SR>NoP8AH2v1uKB(kIxq#*5|iFF(OjI zii$Bs$fcDR1vItD^NxRI5!$M%K^5B_lhsrG%3OE&iF*5!tu zy)dSiU;B5w{^}&QzoI_m_p26g>CF1#A8C75{GW*CNA46>R75I9B(D=7Dl9Hi4iyR* zZTv?*Y~~5)>S=HEjd|edZubAD+l`6FjVIJ)e(C~r}*IY zf6u{Jj$!-Co!|eq&nRr^sAVnoY1cS4bYL{UwPR(9z$_xD{UvQek!lH@X>ip|_wc~a z4)U>gt>m=fDjSBn_}6C-<3qq&$6&R}P{nY`DJ5PTo#G?^;U2~ho`C+!J3K=R6ffFR zd_eQu&lf@j(|I#*5+Je^Ngb#TK`|u@^ND=?%lESL$v61OmgREx>OMKKkg)xfKGrX* z$Z)kJsSh&L+e4*Xmi`i4xHe|bf#dSgFaCh~(K(Sy)WO5KywMIu&T+TIij%aGVBRcn z-YgfiwKg4`ZmfVcTz~UJ-1G2$F5A3}?WYc~kYp^cmRQyuQ%?iNbBs!*+zX=Z_uUdw5NhVR|Khd=+HyM$^PRb^(T zjug(f3%{HsSlB~zjub^MJ?Wg+WV+VO9mJ$8WRbRBEWB9#9H_L-j}Jpn~fL{&pjrCQ=U|L~$dvU^M~ z-8{&OYD^;wC#z#MBD&CMGBVi5$Y39}W{RR%V;Jb^Mg_6}kx|~gI_9zG$M~yzU)C4* zPw9_#jY4l(7m=a7MUv=SI3gztP|0(o0H+8FV#`kE$7cBB?>;3xPUQSk2dSq)Y+Fh! zA~2{*cPW;ha!I;NvGjJ82|-0w=`O`|$FYPENWIF6E+cFEEP!|)3;=n1aeI!x?xf3pynt2YplyiOOoE3#G# zg`*}fW*Sr}#g3k?l7lr^4#8IeBg{BoXIbluQz0-UMJ+YE|C@FwzvP52TKScP>ExS@|qzc zIX$nc8hT4P+JF4iA*dx_C6_C@#5P%KGq~0TiXs{$_fiys5Q27<9e;YqGji#b^1dDkIhG`-QjQ~ytd+VZElnss zK-s}i3GRPaU5o3hO*?H#b4Th@S?Z1l%&IqxyOVm(MB@&^(%kbLa zX(moAu%Em4bpDgKr26dP7I=e z2LeH1+uA;+YAOAd2(S9~<0xI6dwCz|t(1wKMb!{g2`I61)Ecv--XjI1q!^h570NCT z(}A&>9O?y>q)oS;lkIl731RWbBO<5)@1^8KtreURdH<$Cve0Iy7TzVYhN5;!P$l(w zAR}k7CU>CKvc}e6BSTP;W3zR7%T8h|+WKArBAb8L@tFpMP>@ht07)xPAhhXtNkl}0 zf~QT%-q2mPbd{XWH#3|y1Pu}jnsL@(1r8jWXaB@J%`9PASH#wJL-dqmnnFR#dE%Fy z<)6n+@Yt)zn5aPtbcttOKb$&LRwa*)&2@q?*#zYtA+>ZUA{`6KYD^ke)78f;x7!cb={CQk_-h( z&dP%?9nwceYx0S!e~l~Nf36-sx=)^X>P7wGjwd@su%7K!u7$`G7Hs4;9JTh?i)1x%H2jst8_c1>7!4Goo#lIxr zxbDWA_}0yTMixZQUN?vc>>fSNPv5A^xBtgq@wwmn&D`8DcPK#RCwlyfK0n?Tzp-6P~1v7ZkB-6fSF~fr;#%7y29}74m z`rPQ09@i3Iz3JEWip$@p=Un)4?)%{mee}^s^|o(*Lx2CPf28;BIj(cfOg*AMdg(Yf zed#~y=YH!q*}Z!=AG+cSee$^jY~FggZa-%epZwJiF*=9Vo0(=IkHg5aveM(QWl_|t zF4~qKE#^`!9BC(dr|t>_?;#RSU(-VXnQLTXjgVwsoSQV} z9GH;v-*W+*H*c0}uDON}fB3@y(o^jrip$c|w?dW;^e`~ctF;uZrHTi=?fZN6y8rqt zeO*@i24f}5TDFO_@}sbQQEPSJj$*!N1f`j9>AMD{uP0*9_#78+8pwyy#!xB8c%Ns~ zCqD5@eD99G);)Xn5XUjrS~hLks8O|#$*CF6>+2Ojuf6tKnV6VBRrL@4;Hv^?lB8{Y z)+!Y%x(yQxf%7)^bInKBE6oJ&!(xlkwzorTX6bC&T#{|vy$$Oso*bWz*Lef#(07iKu~ls=x^ zwTu1x_ve$9EW>*bz|7P*zA?q)Pw!=Fa*B~YM>A7iKJK~rtYv)WiZz7U2G$f&2p#n! z_+n5MiX3_KA(AtgsCvj!E<1OC3(n}{;k{GT(!eE~hj`?%=XmnjS7qwR9!^Y-wOiFF z$_H+@fA4Q(xEncp!?4tjj`P*8{fPiL=djjF(yZ~u-bdus{fD^Yt{=0iqV!fGUKq~@ z6}SG|GoUPl;6>Dv+Z2R$yRmrKFMf3R_pG&IoFU&yD5k2Nla~-gx+7L}Iqvw$7_l{+ zzkZO>qtonoW*?_-Sj9khN_TaTa#tS!*WdUBzJKT6@bU9bVX!M=SvBJJ`yS%h)C})F z_iQ5Od4AUq`QiOP;?Mv4JwDoe4fuQ{IAMWPvM<@6<7p-NaufiQaJHRnt z;=LDb<*Y56s8m<*@T1T1$j%*nbn6Pv9O-Aa-XwB{BhwA;eEJ9@r=H4SZ^Zok0*A*Y z=?MuJtgkRr5Bzkr$@{hr@W3B#quk}l8lKiBS9==l13t?Lnb)kDO4>|RCcnKkM5S14 z9IK5AM~tW}j>!Qe08zzqXspf!H}2x-WXcVft*2UX`m& zmiOx9Y>gu)W?8>tP-;n2_Z~hb`;R2dHqmm7tQs&n+*e{DQTg%y1{+7ZcM2ujH9HBMMYVBJT zlLSq?Y}{PcvsP8Ofj7rE-=vLq!dM1Yq8ETI(dTQvkN5NqYBdtbqS%^ko?V9%Ru7fsp0Avx+s+xl&!z>kb#VfngGyRC5<)<|m*BmI)QkPXM~1#+jE#&H z&G#S$1Tmchw_L4>QZ$bBOtx<9mWxm8<)N2m~ZT2O0{UwKbefBVW|hI&hqdMKAI<=7EBLll|3 z=P{y2iXAIeZ6$k_WcX$(X_7weG=zJD_uGs&8Zt4es_G3vMSWp>BMDk-q|7X&%q}D{ zyO8M2e8NIKLqp&fw-_Dnj(O_!l-DK#hu%n3fNCs6Hb+C#t1hH@$2QOf+fOlUUmxk| zBg(E9$FbIGPgg{LPfTxjEYv$;6y;WH#vd zj51YIX{Pzab$N^q^@t4j8XfKx2D?SP20b*HNbE#Qu_enEhlhfa8?Fc{s8z9`f)IkL z58h|q`Ybdw_`el^v0FA?Z>_z>#x9u1su*ikM124slq3z(NIkVi#(X_vzL7G&kTPG- zs5LTeq(PdA$A_Rc-`kfY4J3KZ0s$vT>_nXt3B{hLwIb!%YNcf9Dm&?^L{z&XdMb|Y zvdg0pS)3DfITxvD5Dn^mnu$*`wh3}9Ly}h1Ph-7isYmt)PVP{QSd-?gmMt$Dw`OG&$2wxt#Vc)F)V>fTeZ(rif%^S9e zF;|O`D@A0B7*i1lZ329&-|{FY$%r@qK24YW3hOPm0Xt@6?FPNI7)C>=tLn=d^ga#Y hZ%01A|K(2G{||rEhlWcno5uhE002ovPDHLkV1nCsRa5`~ literal 0 HcmV?d00001 diff --git a/Resources/Icon-Small.png b/Resources/Icon-Small.png new file mode 100644 index 0000000000000000000000000000000000000000..1f11669597845b9ad3577ea779ad2319a10f5cc5 GIT binary patch literal 4956 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 LNkvXXu0mjfBlTdB literal 0 HcmV?d00001 diff --git a/Resources/Icon.png b/Resources/Icon.png new file mode 100644 index 0000000000000000000000000000000000000000..def898328621934d3bdb81958d285dc74c3606c2 GIT binary patch literal 9327 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 + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + Icon files + + Icon.png + Icon@2x.png + Icon-72.png + Icon-Small-50.png + Icon-Small.png + Icon-Small@2x.png + + CFBundleIdentifier + com.yourcompany.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UIPrerenderedIcon + + UIStatusBarHidden + + UIRequiredDeviceCapabilities + + accelerometer + + opengles-1 + + + + diff --git a/Resources/fps_images.png b/Resources/fps_images.png new file mode 100644 index 0000000000000000000000000000000000000000..e91d0af540bf4cb554cee016630fd5eddfed31dc GIT binary patch literal 6203 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 literal 0 HcmV?d00001 diff --git a/Resources/iTunesArtwork b/Resources/iTunesArtwork new file mode 100644 index 0000000000000000000000000000000000000000..b1cc056ba575a346e1bfa3baa189c20efbfe496b GIT binary patch literal 61982 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 SlutY{?~w5D*Pu9-FCCu<>yh` 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 Hu0mjfpM?{b literal 0 HcmV?d00001 diff --git a/libs/CocosDenshion/CDAudioManager.h b/libs/CocosDenshion/CDAudioManager.h new file mode 100644 index 0000000..2475929 --- /dev/null +++ b/libs/CocosDenshion/CDAudioManager.h @@ -0,0 +1,243 @@ +/* + Copyright (c) 2010 Steve Oldmeadow + + 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. + + $Id$ + */ + +#import "CocosDenshion.h" +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 30000 + #import +#else + #import "CDXMacOSXSupport.h" +#endif + +/** Different modes of the engine */ +typedef enum { + kAMM_FxOnly, //!Other apps will be able to play audio + kAMM_FxPlusMusic, //!Only this app will play audio + kAMM_FxPlusMusicIfNoOtherAudio, //!If another app is playing audio at start up then allow it to continue and don't play music + kAMM_MediaPlayback, //!This app takes over audio e.g music player app + kAMM_PlayAndRecord //!App takes over audio and has input and output +} tAudioManagerMode; + +/** Possible states of the engine */ +typedef enum { + kAMStateUninitialised, //!Audio manager has not been initialised - do not use + kAMStateInitialising, //!Audio manager is in the process of initialising - do not use + kAMStateInitialised //!Audio manager is initialised - safe to use +} tAudioManagerState; + +typedef enum { + kAMRBDoNothing, //Audio manager will not do anything on resign or becoming active + kAMRBStopPlay, //Background music is stopped on resign and resumed on become active + kAMRBStop //Background music is stopped on resign but not resumed - maybe because you want to do this from within your game +} tAudioManagerResignBehavior; + +/** Notifications */ +extern NSString * const kCDN_AudioManagerInitialised; + +@interface CDAsynchInitialiser : NSOperation {} +@end + +/** CDAudioManager supports two long audio source channels called left and right*/ +typedef enum { + kASC_Left = 0, + kASC_Right = 1 +} tAudioSourceChannel; + +typedef enum { + kLAS_Init, + kLAS_Loaded, + kLAS_Playing, + kLAS_Paused, + kLAS_Stopped, +} tLongAudioSourceState; + +@class CDLongAudioSource; +@protocol CDLongAudioSourceDelegate +@optional +/** The audio source completed playing */ +- (void) cdAudioSourceDidFinishPlaying:(CDLongAudioSource *) audioSource; +/** The file used to load the audio source has changed */ +- (void) cdAudioSourceFileDidChange:(CDLongAudioSource *) audioSource; +@end + +/** + CDLongAudioSource represents an audio source that has a long duration which makes + it costly to load into memory for playback as an effect using CDSoundEngine. Examples + include background music and narration tracks. The audio file may or may not be compressed. + Bear in mind that current iDevices can only use hardware to decode a single compressed + audio file at a time and playing multiple compressed files will result in a performance drop + as software decompression will take place. + @since v0.99 + */ +@interface CDLongAudioSource : NSObject { + AVAudioPlayer *audioSourcePlayer; + NSString *audioSourceFilePath; + NSInteger numberOfLoops; + float volume; + id delegate; + BOOL mute; + BOOL enabled_; + BOOL backgroundMusic; +@public + BOOL systemPaused;//Used for auto resign handling + NSTimeInterval systemPauseLocation;//Used for auto resign handling +@protected + tLongAudioSourceState state; +} +@property (readonly) AVAudioPlayer *audioSourcePlayer; +@property (readonly) NSString *audioSourceFilePath; +@property (readwrite, nonatomic) NSInteger numberOfLoops; +@property (readwrite, nonatomic) float volume; +@property (assign) id delegate; +/* This long audio source functions as background music */ +@property (readwrite, nonatomic) BOOL backgroundMusic; + +/** Loads the file into the audio source */ +-(void) load:(NSString*) filePath; +/** Plays the audio source */ +-(void) play; +/** Stops playing the audio soruce */ +-(void) stop; +/** Pauses the audio source */ +-(void) pause; +/** Rewinds the audio source */ +-(void) rewind; +/** Resumes playing the audio source if it was paused */ +-(void) resume; +/** Returns whether or not the audio source is playing */ +-(BOOL) isPlaying; + +@end + +/** + CDAudioManager manages audio requirements for a game. It provides access to a CDSoundEngine object + for playing sound effects. It provides access to two CDLongAudioSource object (left and right channel) + for playing long duration audio such as background music and narration tracks. Additionally it manages + the audio session to take care of things like audio session interruption and interacting with the audio + of other apps that are running on the device. + + Requirements: + - Firmware: OS 2.2 or greater + - Files: CDAudioManager.*, CocosDenshion.* + - Frameworks: OpenAL, AudioToolbox, AVFoundation + @since v0.8 + */ +@interface CDAudioManager : NSObject { + CDSoundEngine *soundEngine; + CDLongAudioSource *backgroundMusic; + NSMutableArray *audioSourceChannels; + NSString* _audioSessionCategory; + BOOL _audioWasPlayingAtStartup; + tAudioManagerMode _mode; + SEL backgroundMusicCompletionSelector; + id backgroundMusicCompletionListener; + BOOL willPlayBackgroundMusic; + BOOL _mute; + BOOL _resigned; + BOOL _interrupted; + BOOL _audioSessionActive; + BOOL enabled_; + + //For handling resign/become active + BOOL _isObservingAppEvents; + tAudioManagerResignBehavior _resignBehavior; +} + +@property (readonly) CDSoundEngine *soundEngine; +@property (readonly) CDLongAudioSource *backgroundMusic; +@property (readonly) BOOL willPlayBackgroundMusic; + +/** Returns the shared singleton */ ++ (CDAudioManager *) sharedManager; ++ (tAudioManagerState) sharedManagerState; +/** Configures the shared singleton with a mode*/ ++ (void) configure: (tAudioManagerMode) mode; +/** Initializes the engine asynchronously with a mode */ ++ (void) initAsynchronously: (tAudioManagerMode) mode; +/** Initializes the engine synchronously with a mode, channel definition and a total number of channels */ +- (id) init: (tAudioManagerMode) mode; +-(void) audioSessionInterrupted; +-(void) audioSessionResumed; +-(void) setResignBehavior:(tAudioManagerResignBehavior) resignBehavior autoHandle:(BOOL) autoHandle; +/** Returns true is audio is muted at a hardware level e.g user has ringer switch set to off */ +-(BOOL) isDeviceMuted; +/** Returns true if another app is playing audio such as the iPod music player */ +-(BOOL) isOtherAudioPlaying; +/** Sets the way the audio manager interacts with the operating system such as whether it shares output with other apps or obeys the mute switch */ +-(void) setMode:(tAudioManagerMode) mode; +/** Shuts down the shared audio manager instance so that it can be reinitialised */ ++(void) end; + +/** Call if you want to use built in resign behavior but need to do some additional audio processing on resign active. */ +- (void) applicationWillResignActive; +/** Call if you want to use built in resign behavior but need to do some additional audio processing on become active. */ +- (void) applicationDidBecomeActive; + +//New AVAudioPlayer API +/** Loads the data from the specified file path to the channel's audio source */ +-(CDLongAudioSource*) audioSourceLoad:(NSString*) filePath channel:(tAudioSourceChannel) channel; +/** Retrieves the audio source for the specified channel */ +-(CDLongAudioSource*) audioSourceForChannel:(tAudioSourceChannel) channel; + +//Legacy AVAudioPlayer API +/** Plays music in background. The music can be looped or not + It is recommended to use .aac files as background music since they are decoded by the device (hardware). + */ +-(void) playBackgroundMusic:(NSString*) filePath loop:(BOOL) loop; +/** Preloads a background music */ +-(void) preloadBackgroundMusic:(NSString*) filePath; +/** Stops playing the background music */ +-(void) stopBackgroundMusic; +/** Pauses the background music */ +-(void) pauseBackgroundMusic; +/** Rewinds the background music */ +-(void) rewindBackgroundMusic; +/** Resumes playing the background music */ +-(void) resumeBackgroundMusic; +/** Returns whether or not the background music is playing */ +-(BOOL) isBackgroundMusicPlaying; + +-(void) setBackgroundMusicCompletionListener:(id) listener selector:(SEL) selector; + +@end + +/** Fader for long audio source objects */ +@interface CDLongAudioSourceFader : CDPropertyModifier{} +@end + +static const int kCDNoBuffer = -1; + +/** Allows buffers to be associated with file names */ +@interface CDBufferManager:NSObject{ + NSMutableDictionary* loadedBuffers; + NSMutableArray *freedBuffers; + CDSoundEngine *soundEngine; + int nextBufferId; +} + +-(id) initWithEngine:(CDSoundEngine *) theSoundEngine; +-(int) bufferForFile:(NSString*) filePath create:(BOOL) create; +-(void) releaseBufferForFile:(NSString *) filePath; + +@end + diff --git a/libs/CocosDenshion/CDAudioManager.m b/libs/CocosDenshion/CDAudioManager.m new file mode 100644 index 0000000..0929f3c --- /dev/null +++ b/libs/CocosDenshion/CDAudioManager.m @@ -0,0 +1,887 @@ +/* + Copyright (c) 2010 Steve Oldmeadow + + 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. + + $Id$ + */ + + +#import "CDAudioManager.h" + +NSString * const kCDN_AudioManagerInitialised = @"kCDN_AudioManagerInitialised"; + +//NSOperation object used to asynchronously initialise +@implementation CDAsynchInitialiser + +-(void) main { + [super main]; + [CDAudioManager sharedManager]; +} + +@end + +@implementation CDLongAudioSource + +@synthesize audioSourcePlayer, audioSourceFilePath, delegate, backgroundMusic; + +-(id) init { + if ((self = [super init])) { + state = kLAS_Init; + volume = 1.0f; + mute = NO; + enabled_ = YES; + } + return self; +} + +-(void) dealloc { + CDLOGINFO(@"Denshion::CDLongAudioSource - deallocating %@", self); + [audioSourcePlayer release]; + [audioSourceFilePath release]; + [super dealloc]; +} + +-(void) load:(NSString*) filePath { + //We have alread loaded a file previously, check if we are being asked to load the same file + if (state == kLAS_Init || ![filePath isEqualToString:audioSourceFilePath]) { + CDLOGINFO(@"Denshion::CDLongAudioSource - Loading new audio source %@",filePath); + //New file + if (state != kLAS_Init) { + [audioSourceFilePath release];//Release old file path + [audioSourcePlayer release];//Release old AVAudioPlayer, they can't be reused + } + audioSourceFilePath = [filePath copy]; + NSError *error = nil; + NSString *path = [CDUtilities fullPathFromRelativePath:audioSourceFilePath]; + audioSourcePlayer = [(AVAudioPlayer*)[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error]; + if (error == nil) { + [audioSourcePlayer prepareToPlay]; + audioSourcePlayer.delegate = self; + if (delegate && [delegate respondsToSelector:@selector(cdAudioSourceFileDidChange:)]) { + //Tell our delegate the file has changed + [delegate cdAudioSourceFileDidChange:self]; + } + } else { + CDLOG(@"Denshion::CDLongAudioSource - Error initialising audio player: %@",error); + } + } else { + //Same file - just return it to a consistent state + [self pause]; + [self rewind]; + } + audioSourcePlayer.volume = volume; + audioSourcePlayer.numberOfLoops = numberOfLoops; + state = kLAS_Loaded; +} + +-(void) play { + if (enabled_) { + self->systemPaused = NO; + [audioSourcePlayer play]; + } else { + CDLOGINFO(@"Denshion::CDLongAudioSource long audio source didn't play because it is disabled"); + } +} + +-(void) stop { + [audioSourcePlayer stop]; +} + +-(void) pause { + [audioSourcePlayer pause]; +} + +-(void) rewind { + [audioSourcePlayer setCurrentTime:0]; +} + +-(void) resume { + [audioSourcePlayer play]; +} + +-(BOOL) isPlaying { + if (state != kLAS_Init) { + return [audioSourcePlayer isPlaying]; + } else { + return NO; + } +} + +-(void) setVolume:(float) newVolume +{ + volume = newVolume; + if (state != kLAS_Init && !mute) { + audioSourcePlayer.volume = newVolume; + } +} + +-(float) volume +{ + return volume; +} + +#pragma mark Audio Interrupt Protocol +-(BOOL) mute +{ + return mute; +} + +-(void) setMute:(BOOL) muteValue +{ + if (mute != muteValue) { + if (mute) { + //Turn sound back on + audioSourcePlayer.volume = volume; + } else { + audioSourcePlayer.volume = 0.0f; + } + mute = muteValue; + } +} + +-(BOOL) enabled +{ + return enabled_; +} + +-(void) setEnabled:(BOOL)enabledValue +{ + if (enabledValue != enabled_) { + enabled_ = enabledValue; + if (!enabled_) { + //"Stop" the sounds + [self pause]; + [self rewind]; + } + } +} + +-(NSInteger) numberOfLoops { + return numberOfLoops; +} + +-(void) setNumberOfLoops:(NSInteger) loopCount +{ + audioSourcePlayer.numberOfLoops = loopCount; + numberOfLoops = loopCount; +} + +- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag { + CDLOGINFO(@"Denshion::CDLongAudioSource - audio player finished"); +#if TARGET_IPHONE_SIMULATOR + CDLOGINFO(@"Denshion::CDLongAudioSource - workaround for OpenAL clobbered audio issue"); + //This is a workaround for an issue in all simulators (tested to 3.1.2). Problem is + //that OpenAL audio playback is clobbered when an AVAudioPlayer stops. Workaround + //is to keep the player playing on an endless loop with 0 volume and then when + //it is played again reset the volume and set loop count appropriately. + //NB: this workaround is not foolproof but it is good enough for most situations. + player.numberOfLoops = -1; + player.volume = 0; + [player play]; +#endif + if (delegate && [delegate respondsToSelector:@selector(cdAudioSourceDidFinishPlaying:)]) { + [delegate cdAudioSourceDidFinishPlaying:self]; + } +} + +-(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player { + CDLOGINFO(@"Denshion::CDLongAudioSource - audio player interrupted"); +} + +-(void)audioPlayerEndInterruption:(AVAudioPlayer *)player { + CDLOGINFO(@"Denshion::CDLongAudioSource - audio player resumed"); + if (self.backgroundMusic) { + //Check if background music can play as rules may have changed during + //the interruption. This is to address a specific issue in 4.x when + //fast task switching + if([CDAudioManager sharedManager].willPlayBackgroundMusic) { + [player play]; + } + } else { + [player play]; + } +} + +@end + + +@interface CDAudioManager (PrivateMethods) +-(BOOL) audioSessionSetActive:(BOOL) active; +-(BOOL) audioSessionSetCategory:(NSString*) category; +-(void) badAlContextHandler; +@end + + +@implementation CDAudioManager +#define BACKGROUND_MUSIC_CHANNEL kASC_Left + +@synthesize soundEngine, willPlayBackgroundMusic; +static CDAudioManager *sharedManager; +static tAudioManagerState _sharedManagerState = kAMStateUninitialised; +static tAudioManagerMode configuredMode; +static BOOL configured = FALSE; + +-(BOOL) audioSessionSetActive:(BOOL) active { + NSError *activationError = nil; + if ([[AVAudioSession sharedInstance] setActive:active error:&activationError]) { + _audioSessionActive = active; + CDLOGINFO(@"Denshion::CDAudioManager - Audio session set active %i succeeded", active); + return YES; + } else { + //Failed + CDLOG(@"Denshion::CDAudioManager - Audio session set active %i failed with error %@", active, activationError); + return NO; + } +} + +-(BOOL) audioSessionSetCategory:(NSString*) category { + NSError *categoryError = nil; + if ([[AVAudioSession sharedInstance] setCategory:category error:&categoryError]) { + CDLOGINFO(@"Denshion::CDAudioManager - Audio session set category %@ succeeded", category); + return YES; + } else { + //Failed + CDLOG(@"Denshion::CDAudioManager - Audio session set category %@ failed with error %@", category, categoryError); + return NO; + } +} + +// Init ++ (CDAudioManager *) sharedManager +{ + @synchronized(self) { + if (!sharedManager) { + if (!configured) { + //Set defaults here + configuredMode = kAMM_FxPlusMusicIfNoOtherAudio; + } + sharedManager = [[CDAudioManager alloc] init:configuredMode]; + _sharedManagerState = kAMStateInitialised;//This is only really relevant when using asynchronous initialisation + [[NSNotificationCenter defaultCenter] postNotificationName:kCDN_AudioManagerInitialised object:nil]; + } + } + return sharedManager; +} + ++ (tAudioManagerState) sharedManagerState { + return _sharedManagerState; +} + +/** + * Call this to set up audio manager asynchronously. Initialisation is finished when sharedManagerState == kAMStateInitialised + */ ++ (void) initAsynchronously: (tAudioManagerMode) mode { + @synchronized(self) { + if (_sharedManagerState == kAMStateUninitialised) { + _sharedManagerState = kAMStateInitialising; + [CDAudioManager configure:mode]; + CDAsynchInitialiser *initOp = [[[CDAsynchInitialiser alloc] init] autorelease]; + NSOperationQueue *opQ = [[[NSOperationQueue alloc] init] autorelease]; + [opQ addOperation:initOp]; + } + } +} + ++ (id) alloc +{ + @synchronized(self) { + NSAssert(sharedManager == nil, @"Attempted to allocate a second instance of a singleton."); + return [super alloc]; + } + return nil; +} + +/* + * Call this method before accessing the shared manager in order to configure the shared audio manager + */ ++ (void) configure: (tAudioManagerMode) mode { + configuredMode = mode; + configured = TRUE; +} + +-(BOOL) isOtherAudioPlaying { + UInt32 isPlaying = 0; + UInt32 varSize = sizeof(isPlaying); + AudioSessionGetProperty (kAudioSessionProperty_OtherAudioIsPlaying, &varSize, &isPlaying); + return (isPlaying != 0); +} + +-(void) setMode:(tAudioManagerMode) mode { + + _mode = mode; + switch (_mode) { + + case kAMM_FxOnly: + //Share audio with other app + CDLOGINFO(@"Denshion::CDAudioManager - Audio will be shared"); + //_audioSessionCategory = kAudioSessionCategory_AmbientSound; + _audioSessionCategory = AVAudioSessionCategoryAmbient; + willPlayBackgroundMusic = NO; + break; + + case kAMM_FxPlusMusic: + //Use audio exclusively - if other audio is playing it will be stopped + CDLOGINFO(@"Denshion::CDAudioManager - Audio will be exclusive"); + //_audioSessionCategory = kAudioSessionCategory_SoloAmbientSound; + _audioSessionCategory = AVAudioSessionCategorySoloAmbient; + willPlayBackgroundMusic = YES; + break; + + case kAMM_MediaPlayback: + //Use audio exclusively, ignore mute switch and sleep + CDLOGINFO(@"Denshion::CDAudioManager - Media playback mode, audio will be exclusive"); + //_audioSessionCategory = kAudioSessionCategory_MediaPlayback; + _audioSessionCategory = AVAudioSessionCategoryPlayback; + willPlayBackgroundMusic = YES; + break; + + case kAMM_PlayAndRecord: + //Use audio exclusively, ignore mute switch and sleep, has inputs and outputs + CDLOGINFO(@"Denshion::CDAudioManager - Play and record mode, audio will be exclusive"); + //_audioSessionCategory = kAudioSessionCategory_PlayAndRecord; + _audioSessionCategory = AVAudioSessionCategoryPlayAndRecord; + willPlayBackgroundMusic = YES; + break; + + default: + //kAudioManagerFxPlusMusicIfNoOtherAudio + if ([self isOtherAudioPlaying]) { + CDLOGINFO(@"Denshion::CDAudioManager - Other audio is playing audio will be shared"); + //_audioSessionCategory = kAudioSessionCategory_AmbientSound; + _audioSessionCategory = AVAudioSessionCategoryAmbient; + willPlayBackgroundMusic = NO; + } else { + CDLOGINFO(@"Denshion::CDAudioManager - Other audio is not playing audio will be exclusive"); + //_audioSessionCategory = kAudioSessionCategory_SoloAmbientSound; + _audioSessionCategory = AVAudioSessionCategorySoloAmbient; + willPlayBackgroundMusic = YES; + } + + break; + } + + [self audioSessionSetCategory:_audioSessionCategory]; + +} + +/** + * This method is used to work around various bugs introduced in 4.x OS versions. In some circumstances the + * audio session is interrupted but never resumed, this results in the loss of OpenAL audio when following + * standard practices. If we detect this situation then we will attempt to resume the audio session ourselves. + * Known triggers: lock the device then unlock it (iOS 4.2 gm), playback a song using MPMediaPlayer (iOS 4.0) + */ +- (void) badAlContextHandler { + if (_interrupted && alcGetCurrentContext() == NULL) { + CDLOG(@"Denshion::CDAudioManager - bad OpenAL context detected, attempting to resume audio session"); + [self audioSessionResumed]; + } +} + +- (id) init: (tAudioManagerMode) mode { + if ((self = [super init])) { + + //Initialise the audio session + AVAudioSession* session = [AVAudioSession sharedInstance]; + session.delegate = self; + + _mode = mode; + backgroundMusicCompletionSelector = nil; + _isObservingAppEvents = FALSE; + _mute = NO; + _resigned = NO; + _interrupted = NO; + enabled_ = YES; + _audioSessionActive = NO; + [self setMode:mode]; + soundEngine = [[CDSoundEngine alloc] init]; + + //Set up audioSource channels + audioSourceChannels = [[NSMutableArray alloc] init]; + CDLongAudioSource *leftChannel = [[CDLongAudioSource alloc] init]; + leftChannel.backgroundMusic = YES; + CDLongAudioSource *rightChannel = [[CDLongAudioSource alloc] init]; + rightChannel.backgroundMusic = NO; + [audioSourceChannels insertObject:leftChannel atIndex:kASC_Left]; + [audioSourceChannels insertObject:rightChannel atIndex:kASC_Right]; + [leftChannel release]; + [rightChannel release]; + //Used to support legacy APIs + backgroundMusic = [self audioSourceForChannel:BACKGROUND_MUSIC_CHANNEL]; + backgroundMusic.delegate = self; + + //Add handler for bad al context messages, these are posted by the sound engine. + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(badAlContextHandler) name:kCDN_BadAlContext object:nil]; + + } + return self; +} + +-(void) dealloc { + CDLOGINFO(@"Denshion::CDAudioManager - deallocating"); + [self stopBackgroundMusic]; + [soundEngine release]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [self audioSessionSetActive:NO]; + [audioSourceChannels release]; + [super dealloc]; +} + +/** Retrieves the audio source for the specified channel */ +-(CDLongAudioSource*) audioSourceForChannel:(tAudioSourceChannel) channel +{ + return (CDLongAudioSource*)[audioSourceChannels objectAtIndex:channel]; +} + +/** Loads the data from the specified file path to the channel's audio source */ +-(CDLongAudioSource*) audioSourceLoad:(NSString*) filePath channel:(tAudioSourceChannel) channel +{ + CDLongAudioSource *audioSource = [self audioSourceForChannel:channel]; + if (audioSource) { + [audioSource load:filePath]; + } + return audioSource; +} + +-(BOOL) isBackgroundMusicPlaying { + return [self.backgroundMusic isPlaying]; +} + +//NB: originally I tried using a route change listener and intended to store the current route, +//however, on a 3gs running 3.1.2 no route change is generated when the user switches the +//ringer mute switch to off (i.e. enables sound) therefore polling is the only reliable way to +//determine ringer switch state +-(BOOL) isDeviceMuted { + +#if TARGET_IPHONE_SIMULATOR + //Calling audio route stuff on the simulator causes problems + return NO; +#else + CFStringRef newAudioRoute; + UInt32 propertySize = sizeof (CFStringRef); + + AudioSessionGetProperty ( + kAudioSessionProperty_AudioRoute, + &propertySize, + &newAudioRoute + ); + + if (newAudioRoute == NULL) { + //Don't expect this to happen but playing safe otherwise a null in the CFStringCompare will cause a crash + return YES; + } else { + CFComparisonResult newDeviceIsMuted = CFStringCompare ( + newAudioRoute, + (CFStringRef) @"", + 0 + ); + + return (newDeviceIsMuted == kCFCompareEqualTo); + } +#endif +} + +#pragma mark Audio Interrupt Protocol + +-(BOOL) mute { + return _mute; +} + +-(void) setMute:(BOOL) muteValue { + if (muteValue != _mute) { + _mute = muteValue; + [soundEngine setMute:muteValue]; + for( CDLongAudioSource *audioSource in audioSourceChannels) { + audioSource.mute = muteValue; + } + } +} + +-(BOOL) enabled { + return enabled_; +} + +-(void) setEnabled:(BOOL) enabledValue { + if (enabledValue != enabled_) { + enabled_ = enabledValue; + [soundEngine setEnabled:enabled_]; + for( CDLongAudioSource *audioSource in audioSourceChannels) { + audioSource.enabled = enabled_; + } + } +} + +-(CDLongAudioSource*) backgroundMusic +{ + return backgroundMusic; +} + +//Load background music ready for playing +-(void) preloadBackgroundMusic:(NSString*) filePath +{ + [self.backgroundMusic load:filePath]; +} + +-(void) playBackgroundMusic:(NSString*) filePath loop:(BOOL) loop +{ + [self.backgroundMusic load:filePath]; + + if (!willPlayBackgroundMusic || _mute) { + CDLOGINFO(@"Denshion::CDAudioManager - play bgm aborted because audio is not exclusive or sound is muted"); + return; + } + + if (loop) { + [self.backgroundMusic setNumberOfLoops:-1]; + } else { + [self.backgroundMusic setNumberOfLoops:0]; + } + [self.backgroundMusic play]; +} + +-(void) stopBackgroundMusic +{ + [self.backgroundMusic stop]; +} + +-(void) pauseBackgroundMusic +{ + [self.backgroundMusic pause]; +} + +-(void) resumeBackgroundMusic +{ + if (!willPlayBackgroundMusic || _mute) { + CDLOGINFO(@"Denshion::CDAudioManager - resume bgm aborted because audio is not exclusive or sound is muted"); + return; + } + + [self.backgroundMusic resume]; +} + +-(void) rewindBackgroundMusic +{ + [self.backgroundMusic rewind]; +} + +-(void) setBackgroundMusicCompletionListener:(id) listener selector:(SEL) selector { + backgroundMusicCompletionListener = listener; + backgroundMusicCompletionSelector = selector; +} + +/* + * Call this method to have the audio manager automatically handle application resign and + * become active. Pass a tAudioManagerResignBehavior to indicate the desired behavior + * for resigning and becoming active again. + * + * If autohandle is YES then the applicationWillResignActive and applicationDidBecomActive + * methods are automatically called, otherwise you must call them yourself at the appropriate time. + * + * Based on idea of Dominique Bongard + */ +-(void) setResignBehavior:(tAudioManagerResignBehavior) resignBehavior autoHandle:(BOOL) autoHandle { + + if (!_isObservingAppEvents && autoHandle) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:@"UIApplicationWillResignActiveNotification" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:@"UIApplicationDidBecomeActiveNotification" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) name:@"UIApplicationWillTerminateNotification" object:nil]; + _isObservingAppEvents = TRUE; + } + _resignBehavior = resignBehavior; +} + +- (void) applicationWillResignActive { + self->_resigned = YES; + + //Set the audio sesssion to one that allows sharing so that other audio won't be clobbered on resume + [self audioSessionSetCategory:AVAudioSessionCategoryAmbient]; + + switch (_resignBehavior) { + + case kAMRBStopPlay: + + for( CDLongAudioSource *audioSource in audioSourceChannels) { + if (audioSource.isPlaying) { + audioSource->systemPaused = YES; + audioSource->systemPauseLocation = audioSource.audioSourcePlayer.currentTime; + [audioSource stop]; + } else { + //Music is either paused or stopped, if it is paused it will be restarted + //by OS so we will stop it. + audioSource->systemPaused = NO; + [audioSource stop]; + } + } + break; + + case kAMRBStop: + //Stop music regardless of whether it is playing or not because if it was paused + //then the OS would resume it + for( CDLongAudioSource *audioSource in audioSourceChannels) { + [audioSource stop]; + } + + default: + break; + + } + CDLOGINFO(@"Denshion::CDAudioManager - handled resign active"); +} + +//Called when application resigns active only if setResignBehavior has been called +- (void) applicationWillResignActive:(NSNotification *) notification +{ + [self applicationWillResignActive]; +} + +- (void) applicationDidBecomeActive { + + if (self->_resigned) { + _resigned = NO; + //Reset the mode incase something changed with audio while we were inactive + [self setMode:_mode]; + switch (_resignBehavior) { + + case kAMRBStopPlay: + + //Music had been stopped but stop maintains current time + //so playing again will continue from where music was before resign active. + //We check if music can be played because while we were inactive the user might have + //done something that should force music to not play such as starting a track in the iPod + if (self.willPlayBackgroundMusic) { + for( CDLongAudioSource *audioSource in audioSourceChannels) { + if (audioSource->systemPaused) { + [audioSource resume]; + audioSource->systemPaused = NO; + } + } + } + break; + + default: + break; + + } + CDLOGINFO(@"Denshion::CDAudioManager - audio manager handled become active"); + } +} + +//Called when application becomes active only if setResignBehavior has been called +- (void) applicationDidBecomeActive:(NSNotification *) notification +{ + [self applicationDidBecomeActive]; +} + +//Called when application terminates only if setResignBehavior has been called +- (void) applicationWillTerminate:(NSNotification *) notification +{ + CDLOGINFO(@"Denshion::CDAudioManager - audio manager handling terminate"); + [self stopBackgroundMusic]; +} + +/** The audio source completed playing */ +- (void) cdAudioSourceDidFinishPlaying:(CDLongAudioSource *) audioSource { + CDLOGINFO(@"Denshion::CDAudioManager - audio manager got told background music finished"); + if (backgroundMusicCompletionSelector != nil) { + [backgroundMusicCompletionListener performSelector:backgroundMusicCompletionSelector]; + } +} + +-(void) beginInterruption { + CDLOGINFO(@"Denshion::CDAudioManager - begin interruption"); + [self audioSessionInterrupted]; +} + +-(void) endInterruption { + CDLOGINFO(@"Denshion::CDAudioManager - end interruption"); + [self audioSessionResumed]; +} + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000 +-(void) endInterruptionWithFlags:(NSUInteger)flags { + CDLOGINFO(@"Denshion::CDAudioManager - interruption ended with flags %i",flags); + if (flags == AVAudioSessionInterruptionFlags_ShouldResume) { + [self audioSessionResumed]; + } +} +#endif + +-(void)audioSessionInterrupted +{ + if (!_interrupted) { + CDLOGINFO(@"Denshion::CDAudioManager - Audio session interrupted"); + _interrupted = YES; + + // Deactivate the current audio session + [self audioSessionSetActive:NO]; + + if (alcGetCurrentContext() != NULL) { + CDLOGINFO(@"Denshion::CDAudioManager - Setting OpenAL context to NULL"); + + ALenum error = AL_NO_ERROR; + + // set the current context to NULL will 'shutdown' openAL + alcMakeContextCurrent(NULL); + + if((error = alGetError()) != AL_NO_ERROR) { + CDLOG(@"Denshion::CDAudioManager - Error making context current %x\n", error); + } + #pragma unused(error) + } + } +} + +-(void)audioSessionResumed +{ + if (_interrupted) { + CDLOGINFO(@"Denshion::CDAudioManager - Audio session resumed"); + _interrupted = NO; + + BOOL activationResult = NO; + // Reactivate the current audio session + activationResult = [self audioSessionSetActive:YES]; + + //This code is to handle a problem with iOS 4.0 and 4.01 where reactivating the session can fail if + //task switching is performed too rapidly. A test case that reliably reproduces the issue is to call the + //iPhone and then hang up after two rings (timing may vary ;)) + //Basically we keep waiting and trying to let the OS catch up with itself but the number of tries is + //limited. + if (!activationResult) { + CDLOG(@"Denshion::CDAudioManager - Failure reactivating audio session, will try wait-try cycle"); + int activateCount = 0; + while (!activationResult && activateCount < 10) { + [NSThread sleepForTimeInterval:0.5]; + activationResult = [self audioSessionSetActive:YES]; + activateCount++; + CDLOGINFO(@"Denshion::CDAudioManager - Reactivation attempt %i status = %i",activateCount,activationResult); + } + } + + if (alcGetCurrentContext() == NULL) { + CDLOGINFO(@"Denshion::CDAudioManager - Restoring OpenAL context"); + ALenum error = AL_NO_ERROR; + // Restore open al context + alcMakeContextCurrent([soundEngine openALContext]); + if((error = alGetError()) != AL_NO_ERROR) { + CDLOG(@"Denshion::CDAudioManager - Error making context current%x\n", error); + } + #pragma unused(error) + } + } +} + ++(void) end { + [sharedManager release]; + sharedManager = nil; +} + +@end + +/////////////////////////////////////////////////////////////////////////////////////// +@implementation CDLongAudioSourceFader + +-(void) _setTargetProperty:(float) newVal { + ((CDLongAudioSource*)target).volume = newVal; +} + +-(float) _getTargetProperty { + return ((CDLongAudioSource*)target).volume; +} + +-(void) _stopTarget { + //Pause instead of stop as stop releases resources and causes problems in the simulator + [((CDLongAudioSource*)target) pause]; +} + +-(Class) _allowableType { + return [CDLongAudioSource class]; +} + +@end +/////////////////////////////////////////////////////////////////////////////////////// +@implementation CDBufferManager + +-(id) initWithEngine:(CDSoundEngine *) theSoundEngine { + if ((self = [super init])) { + soundEngine = theSoundEngine; + loadedBuffers = [[NSMutableDictionary alloc] initWithCapacity:CD_BUFFERS_START]; + freedBuffers = [[NSMutableArray alloc] init]; + nextBufferId = 0; + } + return self; +} + +-(void) dealloc { + [loadedBuffers release]; + [freedBuffers release]; + [super dealloc]; +} + +-(int) bufferForFile:(NSString*) filePath create:(BOOL) create { + + NSNumber* soundId = (NSNumber*)[loadedBuffers objectForKey:filePath]; + if(soundId == nil) + { + if (create) { + NSNumber* bufferId = nil; + //First try to get a buffer from the free buffers + if ([freedBuffers count] > 0) { + bufferId = [[[freedBuffers lastObject] retain] autorelease]; + [freedBuffers removeLastObject]; + CDLOGINFO(@"Denshion::CDBufferManager reusing buffer id %i",[bufferId intValue]); + } else { + bufferId = [[NSNumber alloc] initWithInt:nextBufferId]; + [bufferId autorelease]; + CDLOGINFO(@"Denshion::CDBufferManager generating new buffer id %i",[bufferId intValue]); + nextBufferId++; + } + + if ([soundEngine loadBuffer:[bufferId intValue] filePath:filePath]) { + //File successfully loaded + CDLOGINFO(@"Denshion::CDBufferManager buffer loaded %@ %@",bufferId,filePath); + [loadedBuffers setObject:bufferId forKey:filePath]; + return [bufferId intValue]; + } else { + //File didn't load, put buffer id on free list + [freedBuffers addObject:bufferId]; + return kCDNoBuffer; + } + } else { + //No matching buffer was found + return kCDNoBuffer; + } + } else { + return [soundId intValue]; + } +} + +-(void) releaseBufferForFile:(NSString *) filePath { + int bufferId = [self bufferForFile:filePath create:NO]; + if (bufferId != kCDNoBuffer) { + [soundEngine unloadBuffer:bufferId]; + [loadedBuffers removeObjectForKey:filePath]; + NSNumber *freedBufferId = [[NSNumber alloc] initWithInt:bufferId]; + [freedBufferId autorelease]; + [freedBuffers addObject:freedBufferId]; + } +} +@end + + + diff --git a/libs/CocosDenshion/CDConfig.h b/libs/CocosDenshion/CDConfig.h new file mode 100644 index 0000000..2bd8f76 --- /dev/null +++ b/libs/CocosDenshion/CDConfig.h @@ -0,0 +1,60 @@ +/* + Copyright (c) 2010 Steve Oldmeadow + + 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. + + $Id$ + */ +#define COCOSDENSHION_VERSION "Aphex.rc" + + +/** + If enabled code useful for debugging such as parameter check assertions will be performed. + If you experience any problems you should enable this and test your code with a debug build. + */ +//#define CD_DEBUG 1 + +/** + The total number of sounds/buffers that can be loaded assuming memory is sufficient + */ +//Number of buffers slots that will be initially created +#define CD_BUFFERS_START 64 +//Number of buffers that will be added +#define CD_BUFFERS_INCREMENT 16 + +/** + If enabled, OpenAL code will use static buffers. When static buffers are used the audio + data is managed outside of OpenAL, this eliminates a memcpy operation which leads to + higher performance when loading sounds. + + However, the downside is that when the audio data is freed you must + be certain that it is no longer being accessed otherwise your app will crash. Testing on OS 2.2.1 + and 3.1.2 has shown that this may occur if a buffer is being used by a source with state = AL_PLAYING + when the buffer is deleted. If the data is freed too quickly after the source is stopped then + a crash will occur. The implemented workaround is that when static buffers are used the unloadBuffer code will wait for + any playing sources to finish playing before the associated buffer and data are deleted, however, this delay may negate any + performance gains that are achieved during loading. + + Performance tests on a 1st gen iPod running OS 2.2.1 loading the CocosDenshionDemo sounds were ~0.14 seconds without + static buffers and ~0.12 seconds when using static buffers. + + */ +//#define CD_USE_STATIC_BUFFERS 1 + + diff --git a/libs/CocosDenshion/CDOpenALSupport.h b/libs/CocosDenshion/CDOpenALSupport.h new file mode 100644 index 0000000..661c69e --- /dev/null +++ b/libs/CocosDenshion/CDOpenALSupport.h @@ -0,0 +1,77 @@ +/* + + Disclaimer: IMPORTANT: This Apple software is supplied to you by + Apple Inc. ("Apple") in consideration of your agreement to the + following terms, and your use, installation, modification or + redistribution of this Apple software constitutes acceptance of these + terms. If you do not agree with these terms, please do not use, + install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. + may be used to endorse or promote products derived from the Apple + Software without specific prior written permission from Apple. Except + as expressly stated in this notice, no other rights or licenses, express + or implied, are granted by Apple herein, including but not limited to + any patent rights that may be infringed by your derivative works or by + other works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2009 Apple Inc. All Rights Reserved. + + $Id$ + */ + +/* + This file contains code from version 1.1 and 1.4 of MyOpenALSupport.h taken from Apple's oalTouch version. + The 1.4 version code is used for loading IMA4 files, however, this code causes very noticeable clicking + when used to load wave files that are looped so the 1.1 version code is used specifically for loading + wav files. + */ + +#ifndef __CD_OPENAL_H +#define __CD_OPENAL_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#import +#import +#import + + +//Taken from oalTouch MyOpenALSupport 1.1 +void* CDloadWaveAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate); +void* CDloadCafAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate); +void* CDGetOpenALAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate); + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/libs/CocosDenshion/CDOpenALSupport.m b/libs/CocosDenshion/CDOpenALSupport.m new file mode 100644 index 0000000..ab0df8e --- /dev/null +++ b/libs/CocosDenshion/CDOpenALSupport.m @@ -0,0 +1,246 @@ +/* + + Disclaimer: IMPORTANT: This Apple software is supplied to you by + Apple Inc. ("Apple") in consideration of your agreement to the + following terms, and your use, installation, modification or + redistribution of this Apple software constitutes acceptance of these + terms. If you do not agree with these terms, please do not use, + install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. + may be used to endorse or promote products derived from the Apple + Software without specific prior written permission from Apple. Except + as expressly stated in this notice, no other rights or licenses, express + or implied, are granted by Apple herein, including but not limited to + any patent rights that may be infringed by your derivative works or by + other works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2009 Apple Inc. All Rights Reserved. + + $Id: CDOpenALSupport.h 16 2010-03-11 06:22:10Z steveoldmeadow $ + */ + +#import "CDOpenALSupport.h" +#import "CocosDenshion.h" +#import +#import + +//Taken from oalTouch MyOpenALSupport 1.1 +void* CDloadWaveAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate) +{ + OSStatus err = noErr; + UInt64 fileDataSize = 0; + AudioStreamBasicDescription theFileFormat; + UInt32 thePropertySize = sizeof(theFileFormat); + AudioFileID afid = 0; + void* theData = NULL; + + // Open a file with ExtAudioFileOpen() + err = AudioFileOpenURL(inFileURL, kAudioFileReadPermission, 0, &afid); + if(err) { CDLOG(@"MyGetOpenALAudioData: AudioFileOpenURL FAILED, Error = %ld\n", err); goto Exit; } + + // Get the audio data format + err = AudioFileGetProperty(afid, kAudioFilePropertyDataFormat, &thePropertySize, &theFileFormat); + if(err) { CDLOG(@"MyGetOpenALAudioData: AudioFileGetProperty(kAudioFileProperty_DataFormat) FAILED, Error = %ld\n", err); goto Exit; } + + if (theFileFormat.mChannelsPerFrame > 2) { + CDLOG(@"MyGetOpenALAudioData - Unsupported Format, channel count is greater than stereo\n"); goto Exit; + } + + if ((theFileFormat.mFormatID != kAudioFormatLinearPCM) || (!TestAudioFormatNativeEndian(theFileFormat))) { + CDLOG(@"MyGetOpenALAudioData - Unsupported Format, must be little-endian PCM\n"); goto Exit; + } + + if ((theFileFormat.mBitsPerChannel != 8) && (theFileFormat.mBitsPerChannel != 16)) { + CDLOG(@"MyGetOpenALAudioData - Unsupported Format, must be 8 or 16 bit PCM\n"); goto Exit; + } + + + thePropertySize = sizeof(fileDataSize); + err = AudioFileGetProperty(afid, kAudioFilePropertyAudioDataByteCount, &thePropertySize, &fileDataSize); + if(err) { CDLOG(@"MyGetOpenALAudioData: AudioFileGetProperty(kAudioFilePropertyAudioDataByteCount) FAILED, Error = %ld\n", err); goto Exit; } + + // Read all the data into memory + UInt32 dataSize = (UInt32)fileDataSize; + theData = malloc(dataSize); + if (theData) + { + AudioFileReadBytes(afid, false, 0, &dataSize, theData); + if(err == noErr) + { + // success + *outDataSize = (ALsizei)dataSize; + //This fix was added by me, however, 8 bit sounds have a clipping sound at the end so aren't really usable (SO) + if (theFileFormat.mBitsPerChannel == 16) { + *outDataFormat = (theFileFormat.mChannelsPerFrame > 1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16; + } else { + *outDataFormat = (theFileFormat.mChannelsPerFrame > 1) ? AL_FORMAT_STEREO8 : AL_FORMAT_MONO8; + } + *outSampleRate = (ALsizei)theFileFormat.mSampleRate; + } + else + { + // failure + free (theData); + theData = NULL; // make sure to return NULL + CDLOG(@"MyGetOpenALAudioData: ExtAudioFileRead FAILED, Error = %ld\n", err); goto Exit; + } + } + +Exit: + // Dispose the ExtAudioFileRef, it is no longer needed + if (afid) AudioFileClose(afid); + return theData; +} + +//Taken from oalTouch MyOpenALSupport 1.4 +void* CDloadCafAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate) +{ + OSStatus status = noErr; + BOOL abort = NO; + SInt64 theFileLengthInFrames = 0; + AudioStreamBasicDescription theFileFormat; + UInt32 thePropertySize = sizeof(theFileFormat); + ExtAudioFileRef extRef = NULL; + void* theData = NULL; + AudioStreamBasicDescription theOutputFormat; + UInt32 dataSize = 0; + + // Open a file with ExtAudioFileOpen() + status = ExtAudioFileOpenURL(inFileURL, &extRef); + if (status != noErr) + { + CDLOG(@"MyGetOpenALAudioData: ExtAudioFileOpenURL FAILED, Error = %ld\n", status); + abort = YES; + } + if (abort) + goto Exit; + + // Get the audio data format + status = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileDataFormat, &thePropertySize, &theFileFormat); + if (status != noErr) + { + CDLOG(@"MyGetOpenALAudioData: ExtAudioFileGetProperty(kExtAudioFileProperty_FileDataFormat) FAILED, Error = %ld\n", status); + abort = YES; + } + if (abort) + goto Exit; + + if (theFileFormat.mChannelsPerFrame > 2) + { + CDLOG(@"MyGetOpenALAudioData - Unsupported Format, channel count is greater than stereo\n"); + abort = YES; + } + if (abort) + goto Exit; + + // Set the client format to 16 bit signed integer (native-endian) data + // Maintain the channel count and sample rate of the original source format + theOutputFormat.mSampleRate = theFileFormat.mSampleRate; + theOutputFormat.mChannelsPerFrame = theFileFormat.mChannelsPerFrame; + + theOutputFormat.mFormatID = kAudioFormatLinearPCM; + theOutputFormat.mBytesPerPacket = 2 * theOutputFormat.mChannelsPerFrame; + theOutputFormat.mFramesPerPacket = 1; + theOutputFormat.mBytesPerFrame = 2 * theOutputFormat.mChannelsPerFrame; + theOutputFormat.mBitsPerChannel = 16; + theOutputFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger; + + // Set the desired client (output) data format + status = ExtAudioFileSetProperty(extRef, kExtAudioFileProperty_ClientDataFormat, sizeof(theOutputFormat), &theOutputFormat); + if (status != noErr) + { + CDLOG(@"MyGetOpenALAudioData: ExtAudioFileSetProperty(kExtAudioFileProperty_ClientDataFormat) FAILED, Error = %ld\n", status); + abort = YES; + } + if (abort) + goto Exit; + + // Get the total frame count + thePropertySize = sizeof(theFileLengthInFrames); + status = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileLengthFrames, &thePropertySize, &theFileLengthInFrames); + if (status != noErr) + { + CDLOG(@"MyGetOpenALAudioData: ExtAudioFileGetProperty(kExtAudioFileProperty_FileLengthFrames) FAILED, Error = %ld\n", status); + abort = YES; + } + if (abort) + goto Exit; + + // Read all the data into memory + dataSize = (UInt32) theFileLengthInFrames * theOutputFormat.mBytesPerFrame; + theData = malloc(dataSize); + if (theData) + { + AudioBufferList theDataBuffer; + theDataBuffer.mNumberBuffers = 1; + theDataBuffer.mBuffers[0].mDataByteSize = dataSize; + theDataBuffer.mBuffers[0].mNumberChannels = theOutputFormat.mChannelsPerFrame; + theDataBuffer.mBuffers[0].mData = theData; + + // Read the data into an AudioBufferList + status = ExtAudioFileRead(extRef, (UInt32*)&theFileLengthInFrames, &theDataBuffer); + if(status == noErr) + { + // success + *outDataSize = (ALsizei)dataSize; + *outDataFormat = (theOutputFormat.mChannelsPerFrame > 1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16; + *outSampleRate = (ALsizei)theOutputFormat.mSampleRate; + } + else + { + // failure + free (theData); + theData = NULL; // make sure to return NULL + CDLOG(@"MyGetOpenALAudioData: ExtAudioFileRead FAILED, Error = %ld\n", status); + abort = YES; + } + } + if (abort) + goto Exit; + +Exit: + // Dispose the ExtAudioFileRef, it is no longer needed + if (extRef) ExtAudioFileDispose(extRef); + return theData; +} + +void* CDGetOpenALAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate) { + + CFStringRef extension = CFURLCopyPathExtension(inFileURL); + CFComparisonResult isWavFile = 0; + if (extension != NULL) { + isWavFile = CFStringCompare (extension,(CFStringRef)@"wav", kCFCompareCaseInsensitive); + CFRelease(extension); + } + + if (isWavFile == kCFCompareEqualTo) { + return CDloadWaveAudioData(inFileURL, outDataSize, outDataFormat, outSampleRate); + } else { + return CDloadCafAudioData(inFileURL, outDataSize, outDataFormat, outSampleRate); + } +} + diff --git a/libs/CocosDenshion/CocosDenshion.h b/libs/CocosDenshion/CocosDenshion.h new file mode 100644 index 0000000..b0a66e4 --- /dev/null +++ b/libs/CocosDenshion/CocosDenshion.h @@ -0,0 +1,440 @@ +/* + Copyright (c) 2010 Steve Oldmeadow + + 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. + + $Id$ + */ + + + +/** +@file +@b IMPORTANT +There are 3 different ways of using CocosDenshion. Depending on which you choose you +will need to include different files and frameworks. + +@par SimpleAudioEngine +This is recommended for basic audio requirements. If you just want to play some sound fx +and some background music and have no interest in learning the lower level workings then +this is the interface to use. + +Requirements: + - Firmware: OS 2.2 or greater + - Files: SimpleAudioEngine.*, CocosDenshion.* + - Frameworks: OpenAL, AudioToolbox, AVFoundation + +@par CDAudioManager +CDAudioManager is basically a thin wrapper around an AVAudioPlayer object used for playing +background music and a CDSoundEngine object used for playing sound effects. It manages the +audio session for you deals with audio session interruption. It is fairly low level and it +is expected you have some understanding of the underlying technologies. For example, for +many use cases regarding background music it is expected you will work directly with the +backgroundMusic AVAudioPlayer which is exposed as a property. + +Requirements: + - Firmware: OS 2.2 or greater + - Files: CDAudioManager.*, CocosDenshion.* + - Frameworks: OpenAL, AudioToolbox, AVFoundation + +@par CDSoundEngine +CDSoundEngine is a sound engine built upon OpenAL and derived from Apple's oalTouch +example. It can playback up to 32 sounds simultaneously with control over pitch, pan +and gain. It can be set up to handle audio session interruption automatically. You +may decide to use CDSoundEngine directly instead of CDAudioManager or SimpleAudioEngine +because you require OS 2.0 compatibility. + +Requirements: + - Firmware: OS 2.0 or greater + - Files: CocosDenshion.* + - Frameworks: OpenAL, AudioToolbox + +*/ + +#import +#import +#import +#import +#import "CDConfig.h" + + +#if !defined(CD_DEBUG) || CD_DEBUG == 0 +#define CDLOG(...) do {} while (0) +#define CDLOGINFO(...) do {} while (0) + +#elif CD_DEBUG == 1 +#define CDLOG(...) NSLog(__VA_ARGS__) +#define CDLOGINFO(...) do {} while (0) + +#elif CD_DEBUG > 1 +#define CDLOG(...) NSLog(__VA_ARGS__) +#define CDLOGINFO(...) NSLog(__VA_ARGS__) +#endif // CD_DEBUG + + +#import "CDOpenALSupport.h" + +//Tested source limit on 2.2.1 and 3.1.2 with up to 128 sources and appears to work. Older OS versions e.g 2.2 may support only 32 +#define CD_SOURCE_LIMIT 32 //Total number of sources we will ever want, may actually get less +#define CD_NO_SOURCE 0xFEEDFAC //Return value indicating playback failed i.e. no source +#define CD_IGNORE_AUDIO_SESSION 0xBEEFBEE //Used internally to indicate audio session will not be handled +#define CD_MUTE 0xFEEDBAB //Return value indicating sound engine is muted or non functioning +#define CD_NO_SOUND = -1; + +#define CD_SAMPLE_RATE_HIGH 44100 +#define CD_SAMPLE_RATE_MID 22050 +#define CD_SAMPLE_RATE_LOW 16000 +#define CD_SAMPLE_RATE_BASIC 8000 +#define CD_SAMPLE_RATE_DEFAULT 44100 + +extern NSString * const kCDN_BadAlContext; +extern NSString * const kCDN_AsynchLoadComplete; + +extern float const kCD_PitchDefault; +extern float const kCD_PitchLowerOneOctave; +extern float const kCD_PitchHigherOneOctave; +extern float const kCD_PanDefault; +extern float const kCD_PanFullLeft; +extern float const kCD_PanFullRight; +extern float const kCD_GainDefault; + +enum bufferState { + CD_BS_EMPTY = 0, + CD_BS_LOADED = 1, + CD_BS_FAILED = 2 +}; + +typedef struct _sourceGroup { + int startIndex; + int currentIndex; + int totalSources; + bool enabled; + bool nonInterruptible; + int *sourceStatuses;//pointer into array of source status information +} sourceGroup; + +typedef struct _bufferInfo { + ALuint bufferId; + int bufferState; + void* bufferData; + ALenum format; + ALsizei sizeInBytes; + ALsizei frequencyInHertz; +} bufferInfo; + +typedef struct _sourceInfo { + bool usable; + ALuint sourceId; + ALuint attachedBufferId; +} sourceInfo; + +#pragma mark CDAudioTransportProtocol + +@protocol CDAudioTransportProtocol +/** Play the audio */ +-(BOOL) play; +/** Pause the audio, retain resources */ +-(BOOL) pause; +/** Stop the audio, release resources */ +-(BOOL) stop; +/** Return playback to beginning */ +-(BOOL) rewind; +@end + +#pragma mark CDAudioInterruptProtocol + +@protocol CDAudioInterruptProtocol +/** Is audio mute */ +-(BOOL) mute; +/** If YES then audio is silenced but not stopped, calls to start new audio will proceed but silently */ +-(void) setMute:(BOOL) muteValue; +/** Is audio enabled */ +-(BOOL) enabled; +/** If NO then all audio is stopped and any calls to start new audio will be ignored */ +-(void) setEnabled:(BOOL) enabledValue; +@end + +#pragma mark CDUtilities +/** + Collection of utilities required by CocosDenshion + */ +@interface CDUtilities : NSObject +{ +} + +/** Fundamentally the same as the corresponding method is CCFileUtils but added to break binding to cocos2d */ ++(NSString*) fullPathFromRelativePath:(NSString*) relPath; + +@end + + +#pragma mark CDSoundEngine + +/** CDSoundEngine is built upon OpenAL and works with SDK 2.0. + CDSoundEngine is a sound engine built upon OpenAL and derived from Apple's oalTouch + example. It can playback up to 32 sounds simultaneously with control over pitch, pan + and gain. It can be set up to handle audio session interruption automatically. You + may decide to use CDSoundEngine directly instead of CDAudioManager or SimpleAudioEngine + because you require OS 2.0 compatibility. + + Requirements: + - Firmware: OS 2.0 or greater + - Files: CocosDenshion.* + - Frameworks: OpenAL, AudioToolbox + + @since v0.8 + */ +@class CDSoundSource; +@interface CDSoundEngine : NSObject { + + bufferInfo *_buffers; + sourceInfo *_sources; + sourceGroup *_sourceGroups; + ALCcontext *context; + int _sourceGroupTotal; + UInt32 _audioSessionCategory; + BOOL _handleAudioSession; + ALfloat _preMuteGain; + NSObject *_mutexBufferLoad; + BOOL mute_; + BOOL enabled_; + + ALenum lastErrorCode_; + BOOL functioning_; + float asynchLoadProgress_; + BOOL getGainWorks_; + + //For managing dynamic allocation of sources and buffers + int sourceTotal_; + int bufferTotal; + +} + +@property (readwrite, nonatomic) ALfloat masterGain; +@property (readonly) ALenum lastErrorCode;//Last OpenAL error code that was generated +@property (readonly) BOOL functioning;//Is the sound engine functioning +@property (readwrite) float asynchLoadProgress; +@property (readonly) BOOL getGainWorks;//Does getting the gain for a source work +/** Total number of sources available */ +@property (readonly) int sourceTotal; +/** Total number of source groups that have been defined */ +@property (readonly) int sourceGroupTotal; + +/** Sets the sample rate for the audio mixer. For best performance this should match the sample rate of your audio content */ ++(void) setMixerSampleRate:(Float32) sampleRate; + +/** Initializes the engine with a group definition and a total number of groups */ +-(id)init; + +/** Plays a sound in a channel group with a pitch, pan and gain. The sound could played looped or not */ +-(ALuint) playSound:(int) soundId sourceGroupId:(int)sourceGroupId pitch:(float) pitch pan:(float) pan gain:(float) gain loop:(BOOL) loop; + +/** Creates and returns a sound source object for the specified sound within the specified source group. + */ +-(CDSoundSource *) soundSourceForSound:(int) soundId sourceGroupId:(int) sourceGroupId; + +/** Stops playing a sound */ +- (void) stopSound:(ALuint) sourceId; +/** Stops playing a source group */ +- (void) stopSourceGroup:(int) sourceGroupId; +/** Stops all playing sounds */ +-(void) stopAllSounds; +-(void) defineSourceGroups:(NSArray*) sourceGroupDefinitions; +-(void) defineSourceGroups:(int[]) sourceGroupDefinitions total:(int) total; +-(void) setSourceGroupNonInterruptible:(int) sourceGroupId isNonInterruptible:(BOOL) isNonInterruptible; +-(void) setSourceGroupEnabled:(int) sourceGroupId enabled:(BOOL) enabled; +-(BOOL) sourceGroupEnabled:(int) sourceGroupId; +-(BOOL) loadBufferFromData:(int) soundId soundData:(ALvoid*) soundData format:(ALenum) format size:(ALsizei) size freq:(ALsizei) freq; +-(BOOL) loadBuffer:(int) soundId filePath:(NSString*) filePath; +-(void) loadBuffersAsynchronously:(NSArray *) loadRequests; +-(BOOL) unloadBuffer:(int) soundId; +-(ALCcontext *) openALContext; + +/** Returns the duration of the buffer in seconds or a negative value if the buffer id is invalid */ +-(float) bufferDurationInSeconds:(int) soundId; +/** Returns the size of the buffer in bytes or a negative value if the buffer id is invalid */ +-(ALsizei) bufferSizeInBytes:(int) soundId; +/** Returns the sampling frequency of the buffer in hertz or a negative value if the buffer id is invalid */ +-(ALsizei) bufferFrequencyInHertz:(int) soundId; + +/** Used internally, never call unless you know what you are doing */ +-(void) _soundSourcePreRelease:(CDSoundSource *) soundSource; + +@end + +#pragma mark CDSoundSource +/** CDSoundSource is a wrapper around an OpenAL sound source. + It allows you to manipulate properties such as pitch, gain, pan and looping while the + sound is playing. CDSoundSource is based on the old CDSourceWrapper class but with much + added functionality. + + @since v1.0 + */ +@interface CDSoundSource : NSObject { + ALenum lastError; +@public + ALuint _sourceId; + ALuint _sourceIndex; + CDSoundEngine* _engine; + int _soundId; + float _preMuteGain; + BOOL enabled_; + BOOL mute_; +} +@property (readwrite, nonatomic) float pitch; +@property (readwrite, nonatomic) float gain; +@property (readwrite, nonatomic) float pan; +@property (readwrite, nonatomic) BOOL looping; +@property (readonly) BOOL isPlaying; +@property (readwrite, nonatomic) int soundId; +/** Returns the duration of the attached buffer in seconds or a negative value if the buffer is invalid */ +@property (readonly) float durationInSeconds; + +/** Stores the last error code that occurred. Check against AL_NO_ERROR */ +@property (readonly) ALenum lastError; +/** Do not init yourself, get an instance from the sourceForSound factory method on CDSoundEngine */ +-(id)init:(ALuint) theSourceId sourceIndex:(int) index soundEngine:(CDSoundEngine*) engine; + +@end + +#pragma mark CDAudioInterruptTargetGroup + +/** Container for objects that implement audio interrupt protocol i.e. they can be muted and enabled. + Setting mute and enabled for the group propagates to all children. + Designed to be used with your CDSoundSource objects to get them to comply with global enabled and mute settings + if that is what you want to do.*/ +@interface CDAudioInterruptTargetGroup : NSObject { + BOOL mute_; + BOOL enabled_; + NSMutableArray *children_; +} +-(void) addAudioInterruptTarget:(NSObject*) interruptibleTarget; +@end + +#pragma mark CDAsynchBufferLoader + +/** CDAsynchBufferLoader + TODO + */ +@interface CDAsynchBufferLoader : NSOperation { + NSArray *_loadRequests; + CDSoundEngine *_soundEngine; +} + +-(id) init:(NSArray *)loadRequests soundEngine:(CDSoundEngine *) theSoundEngine; + +@end + +#pragma mark CDBufferLoadRequest + +/** CDBufferLoadRequest */ +@interface CDBufferLoadRequest: NSObject +{ + NSString *filePath; + int soundId; + //id loader; +} + +@property (readonly) NSString *filePath; +@property (readonly) int soundId; + +- (id)init:(int) theSoundId filePath:(const NSString *) theFilePath; +@end + +/** Interpolation type */ +typedef enum { + kIT_Linear, //!Straight linear interpolation fade + kIT_SCurve, //!S curved interpolation + kIT_Exponential //!Exponential interpolation +} tCDInterpolationType; + +#pragma mark CDFloatInterpolator +@interface CDFloatInterpolator: NSObject +{ + float start; + float end; + float lastValue; + tCDInterpolationType interpolationType; +} +@property (readwrite, nonatomic) float start; +@property (readwrite, nonatomic) float end; +@property (readwrite, nonatomic) tCDInterpolationType interpolationType; + +/** Return a value between min and max based on t which represents fractional progress where 0 is the start + and 1 is the end */ +-(float) interpolate:(float) t; +-(id) init:(tCDInterpolationType) type startVal:(float) startVal endVal:(float) endVal; + +@end + +#pragma mark CDPropertyModifier + +/** Base class for classes that modify properties such as pitch, pan and gain */ +@interface CDPropertyModifier: NSObject +{ + CDFloatInterpolator *interpolator; + float startValue; + float endValue; + id target; + BOOL stopTargetWhenComplete; + +} +@property (readwrite, nonatomic) BOOL stopTargetWhenComplete; +@property (readwrite, nonatomic) float startValue; +@property (readwrite, nonatomic) float endValue; +@property (readwrite, nonatomic) tCDInterpolationType interpolationType; + +-(id) init:(id) theTarget interpolationType:(tCDInterpolationType) type startVal:(float) startVal endVal:(float) endVal; +/** Set to a fractional value between 0 and 1 where 0 equals the start and 1 equals the end*/ +-(void) modify:(float) t; + +-(void) _setTargetProperty:(float) newVal; +-(float) _getTargetProperty; +-(void) _stopTarget; +-(Class) _allowableType; + +@end + +#pragma mark CDSoundSourceFader + +/** Fader for CDSoundSource objects */ +@interface CDSoundSourceFader : CDPropertyModifier{} +@end + +#pragma mark CDSoundSourcePanner + +/** Panner for CDSoundSource objects */ +@interface CDSoundSourcePanner : CDPropertyModifier{} +@end + +#pragma mark CDSoundSourcePitchBender + +/** Pitch bender for CDSoundSource objects */ +@interface CDSoundSourcePitchBender : CDPropertyModifier{} +@end + +#pragma mark CDSoundEngineFader + +/** Fader for CDSoundEngine objects */ +@interface CDSoundEngineFader : CDPropertyModifier{} +@end + + + + diff --git a/libs/CocosDenshion/CocosDenshion.m b/libs/CocosDenshion/CocosDenshion.m new file mode 100644 index 0000000..876a952 --- /dev/null +++ b/libs/CocosDenshion/CocosDenshion.m @@ -0,0 +1,1598 @@ +/* + Copyright (c) 2010 Steve Oldmeadow + + 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. + + $Id$ + */ + +#import "CocosDenshion.h" + +typedef ALvoid AL_APIENTRY (*alBufferDataStaticProcPtr) (const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq); +ALvoid alBufferDataStaticProc(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq) +{ + static alBufferDataStaticProcPtr proc = NULL; + + if (proc == NULL) { + proc = (alBufferDataStaticProcPtr) alcGetProcAddress(NULL, (const ALCchar*) "alBufferDataStatic"); + } + + if (proc) + proc(bid, format, data, size, freq); + + return; +} + +typedef ALvoid AL_APIENTRY (*alcMacOSXMixerOutputRateProcPtr) (const ALdouble value); +ALvoid alcMacOSXMixerOutputRateProc(const ALdouble value) +{ + static alcMacOSXMixerOutputRateProcPtr proc = NULL; + + if (proc == NULL) { + proc = (alcMacOSXMixerOutputRateProcPtr) alcGetProcAddress(NULL, (const ALCchar*) "alcMacOSXMixerOutputRate"); + } + + if (proc) + proc(value); + + return; +} + +NSString * const kCDN_BadAlContext = @"kCDN_BadAlContext"; +NSString * const kCDN_AsynchLoadComplete = @"kCDN_AsynchLoadComplete"; +float const kCD_PitchDefault = 1.0f; +float const kCD_PitchLowerOneOctave = 0.5f; +float const kCD_PitchHigherOneOctave = 2.0f; +float const kCD_PanDefault = 0.0f; +float const kCD_PanFullLeft = -1.0f; +float const kCD_PanFullRight = 1.0f; +float const kCD_GainDefault = 1.0f; + +@interface CDSoundEngine (PrivateMethods) +-(BOOL) _initOpenAL; +-(void) _testGetGain; +-(void) _dumpSourceGroupsInfo; +-(void) _getSourceIndexForSourceGroup; +-(void) _freeSourceGroups; +-(BOOL) _setUpSourceGroups:(int[]) definitions total:(int) total; +@end + +#pragma mark - +#pragma mark CDUtilities + +@implementation CDUtilities + ++(NSString*) fullPathFromRelativePath:(NSString*) relPath +{ + // do not convert an absolute path (starting with '/') + if(([relPath length] > 0) && ([relPath characterAtIndex:0] == '/')) + { + return relPath; + } + + NSMutableArray *imagePathComponents = [NSMutableArray arrayWithArray:[relPath pathComponents]]; + NSString *file = [imagePathComponents lastObject]; + + [imagePathComponents removeLastObject]; + NSString *imageDirectory = [NSString pathWithComponents:imagePathComponents]; + + NSString *fullpath = [[NSBundle mainBundle] pathForResource:file ofType:nil inDirectory:imageDirectory]; + if (fullpath == nil) + fullpath = relPath; + + return fullpath; +} + +@end + +#pragma mark - +#pragma mark CDSoundEngine + +@implementation CDSoundEngine + +static Float32 _mixerSampleRate; +static BOOL _mixerRateSet = NO; + +@synthesize lastErrorCode = lastErrorCode_; +@synthesize functioning = functioning_; +@synthesize asynchLoadProgress = asynchLoadProgress_; +@synthesize getGainWorks = getGainWorks_; +@synthesize sourceTotal = sourceTotal_; + ++ (void) setMixerSampleRate:(Float32) sampleRate { + _mixerRateSet = YES; + _mixerSampleRate = sampleRate; +} + +- (void) _testGetGain { + float testValue = 0.7f; + ALuint testSourceId = _sources[0].sourceId; + alSourcef(testSourceId, AL_GAIN, 0.0f);//Start from know value + alSourcef(testSourceId, AL_GAIN, testValue); + ALfloat gainVal; + alGetSourcef(testSourceId, AL_GAIN, &gainVal); + getGainWorks_ = (gainVal == testValue); +} + +//Generate sources one at a time until we fail +-(void) _generateSources { + + _sources = (sourceInfo*)malloc( sizeof(_sources[0]) * CD_SOURCE_LIMIT); + BOOL hasFailed = NO; + sourceTotal_ = 0; + alGetError();//Clear error + while (!hasFailed && sourceTotal_ < CD_SOURCE_LIMIT) { + alGenSources(1, &(_sources[sourceTotal_].sourceId)); + if (alGetError() == AL_NO_ERROR) { + //Now try attaching source to null buffer + alSourcei(_sources[sourceTotal_].sourceId, AL_BUFFER, 0); + if (alGetError() == AL_NO_ERROR) { + _sources[sourceTotal_].usable = true; + sourceTotal_++; + } else { + hasFailed = YES; + } + } else { + _sources[sourceTotal_].usable = false; + hasFailed = YES; + } + } + //Mark the rest of the sources as not usable + for (int i=sourceTotal_; i < CD_SOURCE_LIMIT; i++) { + _sources[i].usable = false; + } +} + +-(void) _generateBuffers:(int) startIndex endIndex:(int) endIndex { + if (_buffers) { + alGetError(); + for (int i=startIndex; i <= endIndex; i++) { + alGenBuffers(1, &_buffers[i].bufferId); + _buffers[i].bufferData = NULL; + if (alGetError() == AL_NO_ERROR) { + _buffers[i].bufferState = CD_BS_EMPTY; + } else { + _buffers[i].bufferState = CD_BS_FAILED; + CDLOG(@"Denshion::CDSoundEngine - buffer creation failed %i",i); + } + } + } +} + +/** + * Internal method called during init + */ +- (BOOL) _initOpenAL +{ + //ALenum error; + context = NULL; + ALCdevice *newDevice = NULL; + + //Set the mixer rate for the audio mixer + if (!_mixerRateSet) { + _mixerSampleRate = CD_SAMPLE_RATE_DEFAULT; + } + alcMacOSXMixerOutputRateProc(_mixerSampleRate); + CDLOGINFO(@"Denshion::CDSoundEngine - mixer output rate set to %0.2f",_mixerSampleRate); + + // Create a new OpenAL Device + // Pass NULL to specify the system's default output device + newDevice = alcOpenDevice(NULL); + if (newDevice != NULL) + { + // Create a new OpenAL Context + // The new context will render to the OpenAL Device just created + context = alcCreateContext(newDevice, 0); + if (context != NULL) + { + // Make the new context the Current OpenAL Context + alcMakeContextCurrent(context); + + // Create some OpenAL Buffer Objects + [self _generateBuffers:0 endIndex:bufferTotal-1]; + + // Create some OpenAL Source Objects + [self _generateSources]; + + } + } else { + return FALSE;//No device + } + alGetError();//Clear error + return TRUE; +} + +- (void) dealloc { + + ALCcontext *currentContext = NULL; + ALCdevice *device = NULL; + + [self stopAllSounds]; + + CDLOGINFO(@"Denshion::CDSoundEngine - Deallocing sound engine."); + [self _freeSourceGroups]; + + // Delete the Sources + CDLOGINFO(@"Denshion::CDSoundEngine - deleting sources."); + for (int i=0; i < sourceTotal_; i++) { + alSourcei(_sources[i].sourceId, AL_BUFFER, 0);//Detach from current buffer + alDeleteSources(1, &(_sources[i].sourceId)); + if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) { + CDLOG(@"Denshion::CDSoundEngine - Error deleting source! %x\n", lastErrorCode_); + } + } + + // Delete the Buffers + CDLOGINFO(@"Denshion::CDSoundEngine - deleting buffers."); + for (int i=0; i < bufferTotal; i++) { + alDeleteBuffers(1, &_buffers[i].bufferId); +#ifdef CD_USE_STATIC_BUFFERS + if (_buffers[i].bufferData) { + free(_buffers[i].bufferData); + } +#endif + } + CDLOGINFO(@"Denshion::CDSoundEngine - free buffers."); + free(_buffers); + currentContext = alcGetCurrentContext(); + //Get device for active context + device = alcGetContextsDevice(currentContext); + //Release context + CDLOGINFO(@"Denshion::CDSoundEngine - destroy context."); + alcDestroyContext(currentContext); + //Close device + CDLOGINFO(@"Denshion::CDSoundEngine - close device."); + alcCloseDevice(device); + CDLOGINFO(@"Denshion::CDSoundEngine - free sources."); + free(_sources); + + //Release mutexes + [_mutexBufferLoad release]; + + [super dealloc]; +} + +-(int) sourceGroupTotal { + return _sourceGroupTotal; +} + +-(void) _freeSourceGroups +{ + CDLOGINFO(@"Denshion::CDSoundEngine freeing source groups"); + if(_sourceGroups) { + for (int i=0; i < _sourceGroupTotal; i++) { + if (_sourceGroups[i].sourceStatuses) { + free(_sourceGroups[i].sourceStatuses); + CDLOGINFO(@"Denshion::CDSoundEngine freed source statuses %i",i); + } + } + free(_sourceGroups); + } +} + +-(BOOL) _redefineSourceGroups:(int[]) definitions total:(int) total +{ + if (_sourceGroups) { + //Stop all sounds + [self stopAllSounds]; + //Need to free source groups + [self _freeSourceGroups]; + } + return [self _setUpSourceGroups:definitions total:total]; +} + +-(BOOL) _setUpSourceGroups:(int[]) definitions total:(int) total +{ + _sourceGroups = (sourceGroup *)malloc( sizeof(_sourceGroups[0]) * total); + if(!_sourceGroups) { + CDLOG(@"Denshion::CDSoundEngine - source groups memory allocation failed"); + return NO; + } + + _sourceGroupTotal = total; + int sourceCount = 0; + for (int i=0; i < _sourceGroupTotal; i++) { + + _sourceGroups[i].startIndex = 0; + _sourceGroups[i].currentIndex = _sourceGroups[i].startIndex; + _sourceGroups[i].enabled = false; + _sourceGroups[i].nonInterruptible = false; + _sourceGroups[i].totalSources = definitions[i]; + _sourceGroups[i].sourceStatuses = malloc(sizeof(_sourceGroups[i].sourceStatuses[0]) * _sourceGroups[i].totalSources); + if (_sourceGroups[i].sourceStatuses) { + for (int j=0; j < _sourceGroups[i].totalSources; j++) { + //First bit is used to indicate whether source is locked, index is shifted back 1 bit + _sourceGroups[i].sourceStatuses[j] = (sourceCount + j) << 1; + } + } + sourceCount += definitions[i]; + } + return YES; +} + +-(void) defineSourceGroups:(int[]) sourceGroupDefinitions total:(int) total { + [self _redefineSourceGroups:sourceGroupDefinitions total:total]; +} + +-(void) defineSourceGroups:(NSArray*) sourceGroupDefinitions { + CDLOGINFO(@"Denshion::CDSoundEngine - source groups defined by NSArray."); + int totalDefs = [sourceGroupDefinitions count]; + int* defs = (int *)malloc( sizeof(int) * totalDefs); + int currentIndex = 0; + for (id currentDef in sourceGroupDefinitions) { + if ([currentDef isKindOfClass:[NSNumber class]]) { + defs[currentIndex] = [(NSNumber*)currentDef integerValue]; + CDLOGINFO(@"Denshion::CDSoundEngine - found definition %i.",defs[currentIndex]); + } else { + CDLOG(@"Denshion::CDSoundEngine - warning, did not understand source definition."); + defs[currentIndex] = 0; + } + currentIndex++; + } + [self _redefineSourceGroups:defs total:totalDefs]; + free(defs); +} + +- (id)init +{ + if ((self = [super init])) { + + //Create mutexes + _mutexBufferLoad = [[NSObject alloc] init]; + + asynchLoadProgress_ = 0.0f; + + bufferTotal = CD_BUFFERS_START; + _buffers = (bufferInfo *)malloc( sizeof(_buffers[0]) * bufferTotal); + + // Initialize our OpenAL environment + if ([self _initOpenAL]) { + //Set up the default source group - a single group that contains all the sources + int sourceDefs[1]; + sourceDefs[0] = self.sourceTotal; + [self _setUpSourceGroups:sourceDefs total:1]; + + functioning_ = YES; + //Synchronize premute gain + _preMuteGain = self.masterGain; + mute_ = NO; + enabled_ = YES; + //Test whether get gain works for sources + [self _testGetGain]; + } else { + //Something went wrong with OpenAL + functioning_ = NO; + } + } + + return self; +} + +/** + * Delete the buffer identified by soundId + * @return true if buffer deleted successfully, otherwise false + */ +- (BOOL) unloadBuffer:(int) soundId +{ + //Ensure soundId is within array bounds otherwise memory corruption will occur + if (soundId < 0 || soundId >= bufferTotal) { + CDLOG(@"Denshion::CDSoundEngine - soundId is outside array bounds, maybe you need to increase CD_MAX_BUFFERS"); + return FALSE; + } + + //Before a buffer can be deleted any sources that are attached to it must be stopped + for (int i=0; i < sourceTotal_; i++) { + //Note: tried getting the AL_BUFFER attribute of the source instead but doesn't + //appear to work on a device - just returned zero. + if (_buffers[soundId].bufferId == _sources[i].attachedBufferId) { + + CDLOG(@"Denshion::CDSoundEngine - Found attached source %i %i %i",i,_buffers[soundId].bufferId,_sources[i].sourceId); +#ifdef CD_USE_STATIC_BUFFERS + //When using static buffers a crash may occur if a source is playing with a buffer that is about + //to be deleted even though we stop the source and successfully delete the buffer. Crash is confirmed + //on 2.2.1 and 3.1.2, however, it will only occur if a source is used rapidly after having its prior + //data deleted. To avoid any possibility of the crash we wait for the source to finish playing. + ALint state; + + alGetSourcei(_sources[i].sourceId, AL_SOURCE_STATE, &state); + + if (state == AL_PLAYING) { + CDLOG(@"Denshion::CDSoundEngine - waiting for source to complete playing before removing buffer data"); + alSourcei(_sources[i].sourceId, AL_LOOPING, FALSE);//Turn off looping otherwise loops will never end + while (state == AL_PLAYING) { + alGetSourcei(_sources[i].sourceId, AL_SOURCE_STATE, &state); + usleep(10000); + } + } +#endif + //Stop source and detach + alSourceStop(_sources[i].sourceId); + if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) { + CDLOG(@"Denshion::CDSoundEngine - error stopping source: %x\n", lastErrorCode_); + } + + alSourcei(_sources[i].sourceId, AL_BUFFER, 0);//Attach to "NULL" buffer to detach + if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) { + CDLOG(@"Denshion::CDSoundEngine - error detaching buffer: %x\n", lastErrorCode_); + } else { + //Record that source is now attached to nothing + _sources[i].attachedBufferId = 0; + } + } + } + + alDeleteBuffers(1, &_buffers[soundId].bufferId); + if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) { + CDLOG(@"Denshion::CDSoundEngine - error deleting buffer: %x\n", lastErrorCode_); + _buffers[soundId].bufferState = CD_BS_FAILED; + return FALSE; + } else { +#ifdef CD_USE_STATIC_BUFFERS + //Free previous data, if alDeleteBuffer has returned without error then no + if (_buffers[soundId].bufferData) { + CDLOGINFO(@"Denshion::CDSoundEngine - freeing static data for soundId %i @ %i",soundId,_buffers[soundId].bufferData); + free(_buffers[soundId].bufferData);//Free the old data + _buffers[soundId].bufferData = NULL; + } +#endif + } + + alGenBuffers(1, &_buffers[soundId].bufferId); + if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) { + CDLOG(@"Denshion::CDSoundEngine - error regenerating buffer: %x\n", lastErrorCode_); + _buffers[soundId].bufferState = CD_BS_FAILED; + return FALSE; + } else { + //We now have an empty buffer + _buffers[soundId].bufferState = CD_BS_EMPTY; + CDLOGINFO(@"Denshion::CDSoundEngine - buffer %i successfully unloaded\n",soundId); + return TRUE; + } +} + +/** + * Load buffers asynchronously + * Check asynchLoadProgress for progress. asynchLoadProgress represents fraction of completion. When it equals 1.0 loading + * is complete. NB: asynchLoadProgress is simply based on the number of load requests, it does not take into account + * file sizes. + * @param An array of CDBufferLoadRequest objects + */ +- (void) loadBuffersAsynchronously:(NSArray *) loadRequests { + @synchronized(self) { + asynchLoadProgress_ = 0.0f; + CDAsynchBufferLoader *loaderOp = [[[CDAsynchBufferLoader alloc] init:loadRequests soundEngine:self] autorelease]; + NSOperationQueue *opQ = [[[NSOperationQueue alloc] init] autorelease]; + [opQ addOperation:loaderOp]; + } +} + +-(BOOL) _resizeBuffers:(int) increment { + + void * tmpBufferInfos = realloc( _buffers, sizeof(_buffers[0]) * (bufferTotal + increment) ); + + if(!tmpBufferInfos) { + free(tmpBufferInfos); + return NO; + } else { + _buffers = tmpBufferInfos; + int oldBufferTotal = bufferTotal; + bufferTotal = bufferTotal + increment; + [self _generateBuffers:oldBufferTotal endIndex:bufferTotal-1]; + return YES; + } +} + +-(BOOL) loadBufferFromData:(int) soundId soundData:(ALvoid*) soundData format:(ALenum) format size:(ALsizei) size freq:(ALsizei) freq { + + @synchronized(_mutexBufferLoad) { + + if (!functioning_) { + //OpenAL initialisation has previously failed + CDLOG(@"Denshion::CDSoundEngine - Loading buffer failed because sound engine state != functioning"); + return FALSE; + } + + //Ensure soundId is within array bounds otherwise memory corruption will occur + if (soundId < 0) { + CDLOG(@"Denshion::CDSoundEngine - soundId is negative"); + return FALSE; + } + + if (soundId >= bufferTotal) { + //Need to resize the buffers + int requiredIncrement = CD_BUFFERS_INCREMENT; + while (bufferTotal + requiredIncrement < soundId) { + requiredIncrement += CD_BUFFERS_INCREMENT; + } + CDLOGINFO(@"Denshion::CDSoundEngine - attempting to resize buffers by %i for sound %i",requiredIncrement,soundId); + if (![self _resizeBuffers:requiredIncrement]) { + CDLOG(@"Denshion::CDSoundEngine - buffer resize failed"); + return FALSE; + } + } + + if (soundData) + { + if (_buffers[soundId].bufferState != CD_BS_EMPTY) { + CDLOGINFO(@"Denshion::CDSoundEngine - non empty buffer, regenerating"); + if (![self unloadBuffer:soundId]) { + //Deletion of buffer failed, delete buffer routine has set buffer state and lastErrorCode + return NO; + } + } + +#ifdef CD_DEBUG + //Check that sample rate matches mixer rate and warn if they do not + if (freq != (int)_mixerSampleRate) { + CDLOGINFO(@"Denshion::CDSoundEngine - WARNING sample rate does not match mixer sample rate performance may not be optimal."); + } +#endif + +#ifdef CD_USE_STATIC_BUFFERS + alBufferDataStaticProc(_buffers[soundId].bufferId, format, soundData, size, freq); + _buffers[soundId].bufferData = data;//Save the pointer to the new data +#else + alBufferData(_buffers[soundId].bufferId, format, soundData, size, freq); +#endif + if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) { + CDLOG(@"Denshion::CDSoundEngine - error attaching audio to buffer: %x", lastErrorCode_); + _buffers[soundId].bufferState = CD_BS_FAILED; + return FALSE; + } + } else { + CDLOG(@"Denshion::CDSoundEngine Buffer data is null!"); + _buffers[soundId].bufferState = CD_BS_FAILED; + return FALSE; + } + + _buffers[soundId].format = format; + _buffers[soundId].sizeInBytes = size; + _buffers[soundId].frequencyInHertz = freq; + _buffers[soundId].bufferState = CD_BS_LOADED; + CDLOGINFO(@"Denshion::CDSoundEngine Buffer %i loaded format:%i freq:%i size:%i",soundId,format,freq,size); + return TRUE; + }//end mutex +} + +/** + * Load sound data for later play back. + * @return TRUE if buffer loaded okay for play back otherwise false + */ +- (BOOL) loadBuffer:(int) soundId filePath:(NSString*) filePath +{ + + ALvoid* data; + ALenum format; + ALsizei size; + ALsizei freq; + + CDLOGINFO(@"Denshion::CDSoundEngine - Loading openAL buffer %i %@", soundId, filePath); + + CFURLRef fileURL = nil; + NSString *path = [CDUtilities fullPathFromRelativePath:filePath]; + if (path) { + fileURL = (CFURLRef)[[NSURL fileURLWithPath:path] retain]; + } + + if (fileURL) + { + data = CDGetOpenALAudioData(fileURL, &size, &format, &freq); + CFRelease(fileURL); + BOOL result = [self loadBufferFromData:soundId soundData:data format:format size:size freq:freq]; +#ifndef CD_USE_STATIC_BUFFERS + free(data);//Data can be freed here because alBufferData performs a memcpy +#endif + return result; + } else { + CDLOG(@"Denshion::CDSoundEngine Could not find file!\n"); + //Don't change buffer state here as it will be the same as before method was called + return FALSE; + } +} + +-(BOOL) validateBufferId:(int) soundId { + if (soundId < 0 || soundId >= bufferTotal) { + CDLOGINFO(@"Denshion::CDSoundEngine - validateBufferId buffer outside range %i",soundId); + return NO; + } else if (_buffers[soundId].bufferState != CD_BS_LOADED) { + CDLOGINFO(@"Denshion::CDSoundEngine - validateBufferId invalide buffer state %i",soundId); + return NO; + } else { + return YES; + } +} + +-(float) bufferDurationInSeconds:(int) soundId { + if ([self validateBufferId:soundId]) { + float factor = 0.0f; + switch (_buffers[soundId].format) { + case AL_FORMAT_MONO8: + factor = 1.0f; + break; + case AL_FORMAT_MONO16: + factor = 0.5f; + break; + case AL_FORMAT_STEREO8: + factor = 0.5f; + break; + case AL_FORMAT_STEREO16: + factor = 0.25f; + break; + } + return (float)_buffers[soundId].sizeInBytes/(float)_buffers[soundId].frequencyInHertz * factor; + } else { + return -1.0f; + } +} + +-(ALsizei) bufferSizeInBytes:(int) soundId { + if ([self validateBufferId:soundId]) { + return _buffers[soundId].sizeInBytes; + } else { + return -1.0f; + } +} + +-(ALsizei) bufferFrequencyInHertz:(int) soundId { + if ([self validateBufferId:soundId]) { + return _buffers[soundId].frequencyInHertz; + } else { + return -1.0f; + } +} + +- (ALfloat) masterGain { + if (mute_) { + //When mute the real gain will always be 0 therefore return the preMuteGain value + return _preMuteGain; + } else { + ALfloat gain; + alGetListenerf(AL_GAIN, &gain); + return gain; + } +} + +/** + * Overall gain setting multiplier. e.g 0.5 is half the gain. + */ +- (void) setMasterGain:(ALfloat) newGainValue { + if (mute_) { + _preMuteGain = newGainValue; + } else { + alListenerf(AL_GAIN, newGainValue); + } +} + +#pragma mark CDSoundEngine AudioInterrupt protocol +- (BOOL) mute { + return mute_; +} + +/** + * Setting mute silences all sounds but playing sounds continue to advance playback + */ +- (void) setMute:(BOOL) newMuteValue { + + if (newMuteValue == mute_) { + return; + } + + mute_ = newMuteValue; + if (mute_) { + //Remember what the gain was + _preMuteGain = self.masterGain; + //Set gain to 0 - do not use the property as this will adjust preMuteGain when muted + alListenerf(AL_GAIN, 0.0f); + } else { + //Restore gain to what it was before being muted + self.masterGain = _preMuteGain; + } +} + +- (BOOL) enabled { + return enabled_; +} + +- (void) setEnabled:(BOOL)enabledValue +{ + if (enabled_ == enabledValue) { + return; + } + enabled_ = enabledValue; + if (enabled_ == NO) { + [self stopAllSounds]; + } +} + +-(void) _lockSource:(int) sourceIndex lock:(BOOL) lock { + BOOL found = NO; + for (int i=0; i < _sourceGroupTotal && !found; i++) { + if (_sourceGroups[i].sourceStatuses) { + for (int j=0; j < _sourceGroups[i].totalSources && !found; j++) { + //First bit is used to indicate whether source is locked, index is shifted back 1 bit + if((_sourceGroups[i].sourceStatuses[j] >> 1)==sourceIndex) { + if (lock) { + //Set first bit to lock this source + _sourceGroups[i].sourceStatuses[j] |= 1; + } else { + //Unset first bit to unlock this source + _sourceGroups[i].sourceStatuses[j] &= ~1; + } + found = YES; + } + } + } + } +} + +-(int) _getSourceIndexForSourceGroup:(int)sourceGroupId +{ + //Ensure source group id is valid to prevent memory corruption + if (sourceGroupId < 0 || sourceGroupId >= _sourceGroupTotal) { + CDLOG(@"Denshion::CDSoundEngine invalid source group id %i",sourceGroupId); + return CD_NO_SOURCE; + } + + int sourceIndex = -1;//Using -1 to indicate no source found + BOOL complete = NO; + ALint sourceState = 0; + sourceGroup *thisSourceGroup = &_sourceGroups[sourceGroupId]; + thisSourceGroup->currentIndex = thisSourceGroup->startIndex; + while (!complete) { + //Iterate over sources looking for one that is not locked, first bit indicates if source is locked + if ((thisSourceGroup->sourceStatuses[thisSourceGroup->currentIndex] & 1) == 0) { + //This source is not locked + sourceIndex = thisSourceGroup->sourceStatuses[thisSourceGroup->currentIndex] >> 1;//shift back to get the index + if (thisSourceGroup->nonInterruptible) { + //Check if this source is playing, if so it can't be interrupted + alGetSourcei(_sources[sourceIndex].sourceId, AL_SOURCE_STATE, &sourceState); + if (sourceState != AL_PLAYING) { + //complete = YES; + //Set start index so next search starts at the next position + thisSourceGroup->startIndex = thisSourceGroup->currentIndex + 1; + break; + } else { + sourceIndex = -1;//The source index was no good because the source was playing + } + } else { + //complete = YES; + //Set start index so next search starts at the next position + thisSourceGroup->startIndex = thisSourceGroup->currentIndex + 1; + break; + } + } + thisSourceGroup->currentIndex++; + if (thisSourceGroup->currentIndex >= thisSourceGroup->totalSources) { + //Reset to the beginning + thisSourceGroup->currentIndex = 0; + } + if (thisSourceGroup->currentIndex == thisSourceGroup->startIndex) { + //We have looped around and got back to the start + complete = YES; + } + } + + //Reset start index to beginning if beyond bounds + if (thisSourceGroup->startIndex >= thisSourceGroup->totalSources) { + thisSourceGroup->startIndex = 0; + } + + if (sourceIndex >= 0) { + return sourceIndex; + } else { + return CD_NO_SOURCE; + } + +} + +/** + * Play a sound. + * @param soundId the id of the sound to play (buffer id). + * @param SourceGroupId the source group that will be used to play the sound. + * @param pitch pitch multiplier. e.g 1.0 is unaltered, 0.5 is 1 octave lower. + * @param pan stereo position. -1 is fully left, 0 is centre and 1 is fully right. + * @param gain gain multiplier. e.g. 1.0 is unaltered, 0.5 is half the gain + * @param loop should the sound be looped or one shot. + * @return the id of the source being used to play the sound or CD_MUTE if the sound engine is muted or non functioning + * or CD_NO_SOURCE if a problem occurs setting up the source + * + */ +- (ALuint)playSound:(int) soundId sourceGroupId:(int)sourceGroupId pitch:(float) pitch pan:(float) pan gain:(float) gain loop:(BOOL) loop { + +#ifdef CD_DEBUG + //Sanity check parameters - only in DEBUG + NSAssert(soundId >= 0, @"soundId can not be negative"); + NSAssert(soundId < bufferTotal, @"soundId exceeds limit"); + NSAssert(sourceGroupId >= 0, @"sourceGroupId can not be negative"); + NSAssert(sourceGroupId < _sourceGroupTotal, @"sourceGroupId exceeds limit"); + NSAssert(pitch > 0, @"pitch must be greater than zero"); + NSAssert(pan >= -1 && pan <= 1, @"pan must be between -1 and 1"); + NSAssert(gain >= 0, @"gain can not be negative"); +#endif + //If mute or initialisation has failed or buffer is not loaded then do nothing + if (!enabled_ || !functioning_ || _buffers[soundId].bufferState != CD_BS_LOADED || _sourceGroups[sourceGroupId].enabled) { +#ifdef CD_DEBUG + if (!functioning_) { + CDLOGINFO(@"Denshion::CDSoundEngine - sound playback aborted because sound engine is not functioning"); + } else if (_buffers[soundId].bufferState != CD_BS_LOADED) { + CDLOGINFO(@"Denshion::CDSoundEngine - sound playback aborted because buffer %i is not loaded", soundId); + } +#endif + return CD_MUTE; + } + + int sourceIndex = [self _getSourceIndexForSourceGroup:sourceGroupId];//This method ensures sourceIndex is valid + + if (sourceIndex != CD_NO_SOURCE) { + ALint state; + ALuint source = _sources[sourceIndex].sourceId; + ALuint buffer = _buffers[soundId].bufferId; + alGetError();//Clear the error code + alGetSourcei(source, AL_SOURCE_STATE, &state); + if (state == AL_PLAYING) { + alSourceStop(source); + } + alSourcei(source, AL_BUFFER, buffer);//Attach to sound + alSourcef(source, AL_PITCH, pitch);//Set pitch + alSourcei(source, AL_LOOPING, loop);//Set looping + alSourcef(source, AL_GAIN, gain);//Set gain/volume + float sourcePosAL[] = {pan, 0.0f, 0.0f};//Set position - just using left and right panning + alSourcefv(source, AL_POSITION, sourcePosAL); + alGetError();//Clear the error code + alSourcePlay(source); + if((lastErrorCode_ = alGetError()) == AL_NO_ERROR) { + //Everything was okay + _sources[sourceIndex].attachedBufferId = buffer; + return source; + } else { + if (alcGetCurrentContext() == NULL) { + CDLOGINFO(@"Denshion::CDSoundEngine - posting bad OpenAL context message"); + [[NSNotificationCenter defaultCenter] postNotificationName:kCDN_BadAlContext object:nil]; + } + return CD_NO_SOURCE; + } + } else { + return CD_NO_SOURCE; + } +} + +-(BOOL) _soundSourceAttachToBuffer:(CDSoundSource*) soundSource soundId:(int) soundId { + //Attach the source to the buffer + ALint state; + ALuint source = soundSource->_sourceId; + ALuint buffer = _buffers[soundId].bufferId; + alGetSourcei(source, AL_SOURCE_STATE, &state); + if (state == AL_PLAYING) { + alSourceStop(source); + } + alGetError();//Clear the error code + alSourcei(source, AL_BUFFER, buffer);//Attach to sound data + if((lastErrorCode_ = alGetError()) == AL_NO_ERROR) { + _sources[soundSource->_sourceIndex].attachedBufferId = buffer; + //_sourceBufferAttachments[soundSource->_sourceIndex] = buffer;//Keep track of which + soundSource->_soundId = soundId; + return YES; + } else { + return NO; + } +} + +/** + * Get a sound source for the specified sound in the specified source group + */ +-(CDSoundSource *) soundSourceForSound:(int) soundId sourceGroupId:(int) sourceGroupId +{ + if (!functioning_) { + return nil; + } + //Check if a source is available + int sourceIndex = [self _getSourceIndexForSourceGroup:sourceGroupId]; + if (sourceIndex != CD_NO_SOURCE) { + CDSoundSource *result = [[CDSoundSource alloc] init:_sources[sourceIndex].sourceId sourceIndex:sourceIndex soundEngine:self]; + [self _lockSource:sourceIndex lock:YES]; + //Try to attach to the buffer + if ([self _soundSourceAttachToBuffer:result soundId:soundId]) { + //Set to a known state + result.pitch = 1.0f; + result.pan = 0.0f; + result.gain = 1.0f; + result.looping = NO; + return [result autorelease]; + } else { + //Release the sound source we just created, this will also unlock the source + [result release]; + return nil; + } + } else { + //No available source within that source group + return nil; + } +} + +-(void) _soundSourcePreRelease:(CDSoundSource *) soundSource { + CDLOGINFO(@"Denshion::CDSoundEngine _soundSourcePreRelease %i",soundSource->_sourceIndex); + //Unlock the sound source's source + [self _lockSource:soundSource->_sourceIndex lock:NO]; +} + +/** + * Stop all sounds playing within a source group + */ +- (void) stopSourceGroup:(int) sourceGroupId { + + if (!functioning_ || sourceGroupId >= _sourceGroupTotal || sourceGroupId < 0) { + return; + } + int sourceCount = _sourceGroups[sourceGroupId].totalSources; + for (int i=0; i < sourceCount; i++) { + int sourceIndex = _sourceGroups[sourceGroupId].sourceStatuses[i] >> 1; + alSourceStop(_sources[sourceIndex].sourceId); + } + alGetError();//Clear error in case we stopped any sounds that couldn't be stopped +} + +/** + * Stop a sound playing. + * @param sourceId an OpenAL source identifier i.e. the return value of playSound + */ +- (void)stopSound:(ALuint) sourceId { + if (!functioning_) { + return; + } + alSourceStop(sourceId); + alGetError();//Clear error in case we stopped any sounds that couldn't be stopped +} + +- (void) stopAllSounds { + for (int i=0; i < sourceTotal_; i++) { + alSourceStop(_sources[i].sourceId); + } + alGetError();//Clear error in case we stopped any sounds that couldn't be stopped +} + +/** + * Set a source group as non interruptible. Default is that source groups are interruptible. + * Non interruptible means that if a request to play a sound is made for a source group and there are + * no free sources available then the play request will be ignored and CD_NO_SOURCE will be returned. + */ +- (void) setSourceGroupNonInterruptible:(int) sourceGroupId isNonInterruptible:(BOOL) isNonInterruptible { + //Ensure source group id is valid to prevent memory corruption + if (sourceGroupId < 0 || sourceGroupId >= _sourceGroupTotal) { + CDLOG(@"Denshion::CDSoundEngine setSourceGroupNonInterruptible invalid source group id %i",sourceGroupId); + return; + } + + if (isNonInterruptible) { + _sourceGroups[sourceGroupId].nonInterruptible = true; + } else { + _sourceGroups[sourceGroupId].nonInterruptible = false; + } +} + +/** + * Set the mute property for a source group. If mute is turned on any sounds in that source group + * will be stopped and further sounds in that source group will play. However, turning mute off + * will not restart any sounds that were playing when mute was turned on. Also the mute setting + * for the sound engine must be taken into account. If the sound engine is mute no sounds will play + * no matter what the source group mute setting is. + */ +- (void) setSourceGroupEnabled:(int) sourceGroupId enabled:(BOOL) enabled { + //Ensure source group id is valid to prevent memory corruption + if (sourceGroupId < 0 || sourceGroupId >= _sourceGroupTotal) { + CDLOG(@"Denshion::CDSoundEngine setSourceGroupEnabled invalid source group id %i",sourceGroupId); + return; + } + + if (enabled) { + _sourceGroups[sourceGroupId].enabled = true; + [self stopSourceGroup:sourceGroupId]; + } else { + _sourceGroups[sourceGroupId].enabled = false; + } +} + +/** + * Return the mute property for the source group identified by sourceGroupId + */ +- (BOOL) sourceGroupEnabled:(int) sourceGroupId { + return _sourceGroups[sourceGroupId].enabled; +} + +-(ALCcontext *) openALContext { + return context; +} + +- (void) _dumpSourceGroupsInfo { +#ifdef CD_DEBUG + CDLOGINFO(@"-------------- source Group Info --------------"); + for (int i=0; i < _sourceGroupTotal; i++) { + CDLOGINFO(@"Group: %i start:%i total:%i",i,_sourceGroups[i].startIndex, _sourceGroups[i].totalSources); + CDLOGINFO(@"----- mute:%i nonInterruptible:%i",_sourceGroups[i].enabled, _sourceGroups[i].nonInterruptible); + CDLOGINFO(@"----- Source statuses ----"); + for (int j=0; j < _sourceGroups[i].totalSources; j++) { + CDLOGINFO(@"Source status:%i index=%i locked=%i",j,_sourceGroups[i].sourceStatuses[j] >> 1, _sourceGroups[i].sourceStatuses[j] & 1); + } + } +#endif +} + +@end + +/////////////////////////////////////////////////////////////////////////////////////// +@implementation CDSoundSource + +@synthesize lastError; + +//Macro for handling the al error code +#define CDSOUNDSOURCE_UPDATE_LAST_ERROR (lastError = alGetError()) +#define CDSOUNDSOURCE_ERROR_HANDLER ( CDSOUNDSOURCE_UPDATE_LAST_ERROR == AL_NO_ERROR) + +-(id)init:(ALuint) theSourceId sourceIndex:(int) index soundEngine:(CDSoundEngine*) engine { + if ((self = [super init])) { + _sourceId = theSourceId; + _engine = engine; + _sourceIndex = index; + enabled_ = YES; + mute_ = NO; + _preMuteGain = self.gain; + } + return self; +} + +-(void) dealloc +{ + CDLOGINFO(@"Denshion::CDSoundSource deallocated %i",self->_sourceIndex); + + //Notify sound engine we are about to release + [_engine _soundSourcePreRelease:self]; + [super dealloc]; +} + +- (void) setPitch:(float) newPitchValue { + alSourcef(_sourceId, AL_PITCH, newPitchValue); + CDSOUNDSOURCE_UPDATE_LAST_ERROR; +} + +- (void) setGain:(float) newGainValue { + if (!mute_) { + alSourcef(_sourceId, AL_GAIN, newGainValue); + } else { + _preMuteGain = newGainValue; + } + CDSOUNDSOURCE_UPDATE_LAST_ERROR; +} + +- (void) setPan:(float) newPanValue { + float sourcePosAL[] = {newPanValue, 0.0f, 0.0f};//Set position - just using left and right panning + alSourcefv(_sourceId, AL_POSITION, sourcePosAL); + CDSOUNDSOURCE_UPDATE_LAST_ERROR; + +} + +- (void) setLooping:(BOOL) newLoopingValue { + alSourcei(_sourceId, AL_LOOPING, newLoopingValue); + CDSOUNDSOURCE_UPDATE_LAST_ERROR; + +} + +- (BOOL) isPlaying { + ALint state; + alGetSourcei(_sourceId, AL_SOURCE_STATE, &state); + CDSOUNDSOURCE_UPDATE_LAST_ERROR; + return (state == AL_PLAYING); +} + +- (float) pitch { + ALfloat pitchVal; + alGetSourcef(_sourceId, AL_PITCH, &pitchVal); + CDSOUNDSOURCE_UPDATE_LAST_ERROR; + return pitchVal; +} + +- (float) pan { + ALfloat sourcePosAL[] = {0.0f,0.0f,0.0f}; + alGetSourcefv(_sourceId, AL_POSITION, sourcePosAL); + CDSOUNDSOURCE_UPDATE_LAST_ERROR; + return sourcePosAL[0]; +} + +- (float) gain { + if (!mute_) { + ALfloat val; + alGetSourcef(_sourceId, AL_GAIN, &val); + CDSOUNDSOURCE_UPDATE_LAST_ERROR; + return val; + } else { + return _preMuteGain; + } +} + +- (BOOL) looping { + ALfloat val; + alGetSourcef(_sourceId, AL_LOOPING, &val); + CDSOUNDSOURCE_UPDATE_LAST_ERROR; + return val; +} + +-(BOOL) stop { + alSourceStop(_sourceId); + return CDSOUNDSOURCE_ERROR_HANDLER; +} + +-(BOOL) play { + if (enabled_) { + alSourcePlay(_sourceId); + CDSOUNDSOURCE_UPDATE_LAST_ERROR; + if (lastError != AL_NO_ERROR) { + if (alcGetCurrentContext() == NULL) { + CDLOGINFO(@"Denshion::CDSoundSource - posting bad OpenAL context message"); + [[NSNotificationCenter defaultCenter] postNotificationName:kCDN_BadAlContext object:nil]; + } + return NO; + } else { + return YES; + } + } else { + return NO; + } +} + +-(BOOL) pause { + alSourcePause(_sourceId); + return CDSOUNDSOURCE_ERROR_HANDLER; +} + +-(BOOL) rewind { + alSourceRewind(_sourceId); + return CDSOUNDSOURCE_ERROR_HANDLER; +} + +-(void) setSoundId:(int) soundId { + [_engine _soundSourceAttachToBuffer:self soundId:soundId]; +} + +-(int) soundId { + return _soundId; +} + +-(float) durationInSeconds { + return [_engine bufferDurationInSeconds:_soundId]; +} + +#pragma mark CDSoundSource AudioInterrupt protocol +- (BOOL) mute { + return mute_; +} + +/** + * Setting mute silences all sounds but playing sounds continue to advance playback + */ +- (void) setMute:(BOOL) newMuteValue { + + if (newMuteValue == mute_) { + return; + } + + if (newMuteValue) { + //Remember what the gain was + _preMuteGain = self.gain; + self.gain = 0.0f; + mute_ = newMuteValue;//Make sure this is done after setting the gain property as the setter behaves differently depending on mute value + } else { + //Restore gain to what it was before being muted + mute_ = newMuteValue; + self.gain = _preMuteGain; + } +} + +- (BOOL) enabled { + return enabled_; +} + +- (void) setEnabled:(BOOL)enabledValue +{ + if (enabled_ == enabledValue) { + return; + } + enabled_ = enabledValue; + if (enabled_ == NO) { + [self stop]; + } +} + +@end + +//////////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark CDAudioInterruptTargetGroup + +@implementation CDAudioInterruptTargetGroup + +-(id) init { + if ((self = [super init])) { + children_ = [[NSMutableArray alloc] initWithCapacity:32]; + enabled_ = YES; + mute_ = NO; + } + return self; +} + +-(void) addAudioInterruptTarget:(NSObject*) interruptibleTarget { + //Synchronize child with group settings; + [interruptibleTarget setMute:mute_]; + [interruptibleTarget setEnabled:enabled_]; + [children_ addObject:interruptibleTarget]; +} + +-(void) removeAudioInterruptTarget:(NSObject*) interruptibleTarget { + [children_ removeObjectIdenticalTo:interruptibleTarget]; +} + +- (BOOL) mute { + return mute_; +} + +/** + * Setting mute silences all sounds but playing sounds continue to advance playback + */ +- (void) setMute:(BOOL) newMuteValue { + + if (newMuteValue == mute_) { + return; + } + + for (NSObject* target in children_) { + [target setMute:newMuteValue]; + } +} + +- (BOOL) enabled { + return enabled_; +} + +- (void) setEnabled:(BOOL)enabledValue +{ + if (enabledValue == enabled_) { + return; + } + + for (NSObject* target in children_) { + [target setEnabled:enabledValue]; + } +} + +@end + + + +//////////////////////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark CDAsynchBufferLoader + +@implementation CDAsynchBufferLoader + +-(id) init:(NSArray *)loadRequests soundEngine:(CDSoundEngine *) theSoundEngine { + if ((self = [super init])) { + _loadRequests = loadRequests; + [_loadRequests retain]; + _soundEngine = theSoundEngine; + [_soundEngine retain]; + } + return self; +} + +-(void) main { + CDLOGINFO(@"Denshion::CDAsynchBufferLoader - loading buffers"); + [super main]; + _soundEngine.asynchLoadProgress = 0.0f; + + if ([_loadRequests count] > 0) { + float increment = 1.0f / [_loadRequests count]; + //Iterate over load request and load + for (CDBufferLoadRequest *loadRequest in _loadRequests) { + [_soundEngine loadBuffer:loadRequest.soundId filePath:loadRequest.filePath]; + _soundEngine.asynchLoadProgress += increment; + } + } + + //Completed + _soundEngine.asynchLoadProgress = 1.0f; + [[NSNotificationCenter defaultCenter] postNotificationName:kCDN_AsynchLoadComplete object:nil]; + +} + +-(void) dealloc { + [_loadRequests release]; + [_soundEngine release]; + [super dealloc]; +} + +@end + + +/////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark CDBufferLoadRequest + +@implementation CDBufferLoadRequest + +@synthesize filePath, soundId; + +-(id) init:(int) theSoundId filePath:(const NSString *) theFilePath { + if ((self = [super init])) { + soundId = theSoundId; + filePath = [theFilePath copy];//TODO: is retain necessary or does copy set retain count + [filePath retain]; + } + return self; +} + +-(void) dealloc { + [filePath release]; + [super dealloc]; +} + +@end + +/////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark CDFloatInterpolator + +@implementation CDFloatInterpolator +@synthesize start,end,interpolationType; + +-(float) interpolate:(float) t { + + if (t < 1.0f) { + switch (interpolationType) { + case kIT_Linear: + //Linear interpolation + return ((end - start) * t) + start; + + case kIT_SCurve: + //Cubic s curve t^2 * (3 - 2t) + return ((float)(t * t * (3.0 - (2.0 * t))) * (end - start)) + start; + + case kIT_Exponential: + //Formulas taken from EaseAction + if (end > start) { + //Fade in + float logDelta = (t==0) ? 0 : powf(2, 10 * (t/1 - 1)) - 1 * 0.001f; + return ((end - start) * logDelta) + start; + } else { + //Fade Out + float logDelta = (-powf(2, -10 * t/1) + 1); + return ((end - start) * logDelta) + start; + } + default: + return 0.0f; + } + } else { + return end; + } +} + +-(id) init:(tCDInterpolationType) type startVal:(float) startVal endVal:(float) endVal { + if ((self = [super init])) { + start = startVal; + end = endVal; + interpolationType = type; + } + return self; +} + +@end + +/////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark CDPropertyModifier + +@implementation CDPropertyModifier + +@synthesize stopTargetWhenComplete; + +-(id) init:(id) theTarget interpolationType:(tCDInterpolationType) type startVal:(float) startVal endVal:(float) endVal { + if ((self = [super init])) { + if (target) { + //Release the previous target if there is one + [target release]; + } + target = theTarget; +#if CD_DEBUG + //Check target is of the required type + if (![theTarget isMemberOfClass:[self _allowableType]] ) { + CDLOG(@"Denshion::CDPropertyModifier target is not of type %@",[self _allowableType]); + NSAssert([theTarget isKindOfClass:[CDSoundEngine class]], @"CDPropertyModifier target not of required type"); + } +#endif + [target retain]; + startValue = startVal; + endValue = endVal; + if (interpolator) { + //Release previous interpolator if there is one + [interpolator release]; + } + interpolator = [[CDFloatInterpolator alloc] init:type startVal:startVal endVal:endVal]; + stopTargetWhenComplete = NO; + } + return self; +} + +-(void) dealloc { + CDLOGINFO(@"Denshion::CDPropertyModifier deallocated %@",self); + [target release]; + [interpolator release]; + [super dealloc]; +} + +-(void) modify:(float) t { + if (t < 1.0) { + [self _setTargetProperty:[interpolator interpolate:t]]; + } else { + //At the end + [self _setTargetProperty:endValue]; + if (stopTargetWhenComplete) { + [self _stopTarget]; + } + } +} + +-(float) startValue { + return startValue; +} + +-(void) setStartValue:(float) startVal +{ + startValue = startVal; + interpolator.start = startVal; +} + +-(float) endValue { + return startValue; +} + +-(void) setEndValue:(float) endVal +{ + endValue = endVal; + interpolator.end = endVal; +} + +-(tCDInterpolationType) interpolationType { + return interpolator.interpolationType; +} + +-(void) setInterpolationType:(tCDInterpolationType) interpolationType { + interpolator.interpolationType = interpolationType; +} + +-(void) _setTargetProperty:(float) newVal { + +} + +-(float) _getTargetProperty { + return 0.0f; +} + +-(void) _stopTarget { + +} + +-(Class) _allowableType { + return [NSObject class]; +} +@end + +/////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark CDSoundSourceFader + +@implementation CDSoundSourceFader + +-(void) _setTargetProperty:(float) newVal { + ((CDSoundSource*)target).gain = newVal; +} + +-(float) _getTargetProperty { + return ((CDSoundSource*)target).gain; +} + +-(void) _stopTarget { + [((CDSoundSource*)target) stop]; +} + +-(Class) _allowableType { + return [CDSoundSource class]; +} + +@end + +/////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark CDSoundSourcePanner + +@implementation CDSoundSourcePanner + +-(void) _setTargetProperty:(float) newVal { + ((CDSoundSource*)target).pan = newVal; +} + +-(float) _getTargetProperty { + return ((CDSoundSource*)target).pan; +} + +-(void) _stopTarget { + [((CDSoundSource*)target) stop]; +} + +-(Class) _allowableType { + return [CDSoundSource class]; +} + +@end + +/////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark CDSoundSourcePitchBender + +@implementation CDSoundSourcePitchBender + +-(void) _setTargetProperty:(float) newVal { + ((CDSoundSource*)target).pitch = newVal; +} + +-(float) _getTargetProperty { + return ((CDSoundSource*)target).pitch; +} + +-(void) _stopTarget { + [((CDSoundSource*)target) stop]; +} + +-(Class) _allowableType { + return [CDSoundSource class]; +} + +@end + +/////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark CDSoundEngineFader + +@implementation CDSoundEngineFader + +-(void) _setTargetProperty:(float) newVal { + ((CDSoundEngine*)target).masterGain = newVal; +} + +-(float) _getTargetProperty { + return ((CDSoundEngine*)target).masterGain; +} + +-(void) _stopTarget { + [((CDSoundEngine*)target) stopAllSounds]; +} + +-(Class) _allowableType { + return [CDSoundEngine class]; +} + +@end + + diff --git a/libs/CocosDenshion/SimpleAudioEngine.h b/libs/CocosDenshion/SimpleAudioEngine.h new file mode 100644 index 0000000..35396c6 --- /dev/null +++ b/libs/CocosDenshion/SimpleAudioEngine.h @@ -0,0 +1,90 @@ +/* + Copyright (c) 2010 Steve Oldmeadow + + 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. + + $Id$ + */ + + +#import "CDAudioManager.h" + +/** + A wrapper to the CDAudioManager object. + This is recommended for basic audio requirements. If you just want to play some sound fx + and some background music and have no interest in learning the lower level workings then + this is the interface to use. + + Requirements: + - Firmware: OS 2.2 or greater + - Files: SimpleAudioEngine.*, CocosDenshion.* + - Frameworks: OpenAL, AudioToolbox, AVFoundation + @since v0.8 + */ +@interface SimpleAudioEngine : NSObject { + + BOOL mute_; + BOOL enabled_; +} + +/** Background music volume. Range is 0.0f to 1.0f. This will only have an effect if willPlayBackgroundMusic returns YES */ +@property (readwrite) float backgroundMusicVolume; +/** Effects volume. Range is 0.0f to 1.0f */ +@property (readwrite) float effectsVolume; +/** If NO it indicates background music will not be played either because no background music is loaded or the audio session does not permit it.*/ +@property (readonly) BOOL willPlayBackgroundMusic; + +/** returns the shared instance of the SimpleAudioEngine object */ ++ (SimpleAudioEngine*) sharedEngine; + +/** Preloads a music file so it will be ready to play as background music */ +-(void) preloadBackgroundMusic:(NSString*) filePath; + +/** plays background music in a loop*/ +-(void) playBackgroundMusic:(NSString*) filePath; +/** plays background music, if loop is true the music will repeat otherwise it will be played once */ +-(void) playBackgroundMusic:(NSString*) filePath loop:(BOOL) loop; +/** stops playing background music */ +-(void) stopBackgroundMusic; +/** pauses the background music */ +-(void) pauseBackgroundMusic; +/** resume background music that has been paused */ +-(void) resumeBackgroundMusic; +/** rewind the background music */ +-(void) rewindBackgroundMusic; +/** returns whether or not the background music is playing */ +-(BOOL) isBackgroundMusicPlaying; + +/** plays an audio effect with a file path*/ +-(ALuint) playEffect:(NSString*) filePath; +/** stop a sound that is playing, note you must pass in the soundId that is returned when you started playing the sound with playEffect */ +-(void) stopEffect:(ALuint) soundId; +/** plays an audio effect with a file path, pitch, pan and gain */ +-(ALuint) playEffect:(NSString*) filePath pitch:(Float32) pitch pan:(Float32) pan gain:(Float32) gain; +/** preloads an audio effect */ +-(void) preloadEffect:(NSString*) filePath; +/** unloads an audio effect from memory */ +-(void) unloadEffect:(NSString*) filePath; +/** Gets a CDSoundSource object set up to play the specified file. */ +-(CDSoundSource *) soundSourceForFile:(NSString*) filePath; + +/** Shuts down the shared audio engine instance so that it can be reinitialised */ ++(void) end; + +@end diff --git a/libs/CocosDenshion/SimpleAudioEngine.m b/libs/CocosDenshion/SimpleAudioEngine.m new file mode 100644 index 0000000..cdff26c --- /dev/null +++ b/libs/CocosDenshion/SimpleAudioEngine.m @@ -0,0 +1,220 @@ +/* + Copyright (c) 2010 Steve Oldmeadow + + 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. + + $Id$ + */ + +#import "SimpleAudioEngine.h" + +@implementation SimpleAudioEngine + +static SimpleAudioEngine *sharedEngine = nil; +static CDSoundEngine* soundEngine = nil; +static CDAudioManager *am = nil; +static CDBufferManager *bufferManager = nil; + +// Init ++ (SimpleAudioEngine *) sharedEngine +{ + @synchronized(self) { + if (!sharedEngine) + sharedEngine = [[SimpleAudioEngine alloc] init]; + } + return sharedEngine; +} + ++ (id) alloc +{ + @synchronized(self) { + NSAssert(sharedEngine == nil, @"Attempted to allocate a second instance of a singleton."); + return [super alloc]; + } + return nil; +} + +-(id) init +{ + if((self=[super init])) { + am = [CDAudioManager sharedManager]; + soundEngine = am.soundEngine; + bufferManager = [[CDBufferManager alloc] initWithEngine:soundEngine]; + mute_ = NO; + enabled_ = YES; + } + return self; +} + +// Memory +- (void) dealloc +{ + am = nil; + soundEngine = nil; + bufferManager = nil; + [super dealloc]; +} + ++(void) end +{ + am = nil; + [CDAudioManager end]; + [bufferManager release]; + [sharedEngine release]; + sharedEngine = nil; +} + +#pragma mark SimpleAudioEngine - background music + +-(void) preloadBackgroundMusic:(NSString*) filePath { + [am preloadBackgroundMusic:filePath]; +} + +-(void) playBackgroundMusic:(NSString*) filePath +{ + [am playBackgroundMusic:filePath loop:TRUE]; +} + +-(void) playBackgroundMusic:(NSString*) filePath loop:(BOOL) loop +{ + [am playBackgroundMusic:filePath loop:loop]; +} + +-(void) stopBackgroundMusic +{ + [am stopBackgroundMusic]; +} + +-(void) pauseBackgroundMusic { + [am pauseBackgroundMusic]; +} + +-(void) resumeBackgroundMusic { + [am resumeBackgroundMusic]; +} + +-(void) rewindBackgroundMusic { + [am rewindBackgroundMusic]; +} + +-(BOOL) isBackgroundMusicPlaying { + return [am isBackgroundMusicPlaying]; +} + +-(BOOL) willPlayBackgroundMusic { + return [am willPlayBackgroundMusic]; +} + +#pragma mark SimpleAudioEngine - sound effects + +-(ALuint) playEffect:(NSString*) filePath +{ + return [self playEffect:filePath pitch:1.0f pan:0.0f gain:1.0f]; +} + +-(ALuint) playEffect:(NSString*) filePath pitch:(Float32) pitch pan:(Float32) pan gain:(Float32) gain +{ + int soundId = [bufferManager bufferForFile:filePath create:YES]; + if (soundId != kCDNoBuffer) { + return [soundEngine playSound:soundId sourceGroupId:0 pitch:pitch pan:pan gain:gain loop:false]; + } else { + return CD_MUTE; + } +} + +-(void) stopEffect:(ALuint) soundId { + [soundEngine stopSound:soundId]; +} + +-(void) preloadEffect:(NSString*) filePath +{ + int soundId = [bufferManager bufferForFile:filePath create:YES]; + if (soundId == kCDNoBuffer) { + CDLOG(@"Denshion::SimpleAudioEngine sound failed to preload %@",filePath); + } +} + +-(void) unloadEffect:(NSString*) filePath +{ + CDLOGINFO(@"Denshion::SimpleAudioEngine unloadedEffect %@",filePath); + [bufferManager releaseBufferForFile:filePath]; +} + +#pragma mark Audio Interrupt Protocol +-(BOOL) mute +{ + return mute_; +} + +-(void) setMute:(BOOL) muteValue +{ + if (mute_ != muteValue) { + mute_ = muteValue; + am.mute = mute_; + } +} + +-(BOOL) enabled +{ + return enabled_; +} + +-(void) setEnabled:(BOOL) enabledValue +{ + if (enabled_ != enabledValue) { + enabled_ = enabledValue; + am.enabled = enabled_; + } +} + + +#pragma mark SimpleAudioEngine - BackgroundMusicVolume +-(float) backgroundMusicVolume +{ + return am.backgroundMusic.volume; +} + +-(void) setBackgroundMusicVolume:(float) volume +{ + am.backgroundMusic.volume = volume; +} + +#pragma mark SimpleAudioEngine - EffectsVolume +-(float) effectsVolume +{ + return am.soundEngine.masterGain; +} + +-(void) setEffectsVolume:(float) volume +{ + am.soundEngine.masterGain = volume; +} + +-(CDSoundSource *) soundSourceForFile:(NSString*) filePath { + int soundId = [bufferManager bufferForFile:filePath create:YES]; + if (soundId != kCDNoBuffer) { + CDSoundSource *result = [soundEngine soundSourceForSound:soundId sourceGroupId:0]; + CDLOGINFO(@"Denshion::SimpleAudioEngine sound source created for %@",filePath); + return result; + } else { + return nil; + } +} + +@end diff --git a/libs/FontLabel/FontLabel.h b/libs/FontLabel/FontLabel.h new file mode 100644 index 0000000..6de9c2c --- /dev/null +++ b/libs/FontLabel/FontLabel.h @@ -0,0 +1,44 @@ +// +// FontLabel.h +// FontLabel +// +// Created by Kevin Ballard on 5/8/09. +// Copyright © 2009 Zynga Game Networks +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import + +@class ZFont; +@class ZAttributedString; + +@interface FontLabel : UILabel { + void *reserved; // works around a bug in UILabel + ZFont *zFont; + ZAttributedString *zAttributedText; +} +@property (nonatomic, setter=setCGFont:) CGFontRef cgFont __AVAILABILITY_INTERNAL_DEPRECATED; +@property (nonatomic, assign) CGFloat pointSize __AVAILABILITY_INTERNAL_DEPRECATED; +@property (nonatomic, retain, setter=setZFont:) ZFont *zFont; +// if attributedText is nil, fall back on using the inherited UILabel properties +// if attributedText is non-nil, the font/text/textColor +// in addition, adjustsFontSizeToFitWidth does not work with attributed text +@property (nonatomic, copy) ZAttributedString *zAttributedText; +// -initWithFrame:fontName:pointSize: uses FontManager to look up the font name +- (id)initWithFrame:(CGRect)frame fontName:(NSString *)fontName pointSize:(CGFloat)pointSize; +- (id)initWithFrame:(CGRect)frame zFont:(ZFont *)font; +- (id)initWithFrame:(CGRect)frame font:(CGFontRef)font pointSize:(CGFloat)pointSize __AVAILABILITY_INTERNAL_DEPRECATED; +@end diff --git a/libs/FontLabel/FontLabel.m b/libs/FontLabel/FontLabel.m new file mode 100644 index 0000000..58975b1 --- /dev/null +++ b/libs/FontLabel/FontLabel.m @@ -0,0 +1,195 @@ +// +// FontLabel.m +// FontLabel +// +// Created by Kevin Ballard on 5/8/09. +// Copyright © 2009 Zynga Game Networks +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "FontLabel.h" +#import "FontManager.h" +#import "FontLabelStringDrawing.h" +#import "ZFont.h" + +@interface ZFont (ZFontPrivate) +@property (nonatomic, readonly) CGFloat ratio; +@end + +@implementation FontLabel +@synthesize zFont; +@synthesize zAttributedText; + +- (id)initWithFrame:(CGRect)frame fontName:(NSString *)fontName pointSize:(CGFloat)pointSize { + return [self initWithFrame:frame zFont:[[FontManager sharedManager] zFontWithName:fontName pointSize:pointSize]]; +} + +- (id)initWithFrame:(CGRect)frame zFont:(ZFont *)font { + if ((self = [super initWithFrame:frame])) { + zFont = [font retain]; + } + return self; +} + +- (id)initWithFrame:(CGRect)frame font:(CGFontRef)font pointSize:(CGFloat)pointSize { + return [self initWithFrame:frame zFont:[ZFont fontWithCGFont:font size:pointSize]]; +} + +- (CGFontRef)cgFont { + return self.zFont.cgFont; +} + +- (void)setCGFont:(CGFontRef)font { + if (self.zFont.cgFont != font) { + self.zFont = [ZFont fontWithCGFont:font size:self.zFont.pointSize]; + } +} + +- (CGFloat)pointSize { + return self.zFont.pointSize; +} + +- (void)setPointSize:(CGFloat)pointSize { + if (self.zFont.pointSize != pointSize) { + self.zFont = [ZFont fontWithCGFont:self.zFont.cgFont size:pointSize]; + } +} + +- (void)setZAttributedText:(ZAttributedString *)attStr { + if (zAttributedText != attStr) { + [zAttributedText release]; + zAttributedText = [attStr copy]; + [self setNeedsDisplay]; + } +} + +- (void)drawTextInRect:(CGRect)rect { + if (self.zFont == NULL && self.zAttributedText == nil) { + [super drawTextInRect:rect]; + return; + } + + if (self.zAttributedText == nil) { + // this method is documented as setting the text color for us, but that doesn't appear to be the case + if (self.highlighted) { + [(self.highlightedTextColor ?: [UIColor whiteColor]) setFill]; + } else { + [(self.textColor ?: [UIColor blackColor]) setFill]; + } + + ZFont *actualFont = self.zFont; + CGSize origSize = rect.size; + if (self.numberOfLines == 1) { + origSize.height = actualFont.leading; + CGPoint point = CGPointMake(rect.origin.x, + rect.origin.y + roundf(((rect.size.height - actualFont.leading) / 2.0f))); + CGSize size = [self.text sizeWithZFont:actualFont]; + if (self.adjustsFontSizeToFitWidth && self.minimumFontSize < actualFont.pointSize) { + if (size.width > origSize.width) { + CGFloat desiredRatio = (origSize.width * actualFont.ratio) / size.width; + CGFloat desiredPointSize = desiredRatio * actualFont.pointSize / actualFont.ratio; + actualFont = [actualFont fontWithSize:MAX(MAX(desiredPointSize, self.minimumFontSize), 1.0f)]; + size = [self.text sizeWithZFont:actualFont]; + } + if (!CGSizeEqualToSize(origSize, size)) { + switch (self.baselineAdjustment) { + case UIBaselineAdjustmentAlignCenters: + point.y += roundf((origSize.height - size.height) / 2.0f); + break; + case UIBaselineAdjustmentAlignBaselines: + point.y += (self.zFont.ascender - actualFont.ascender); + break; + case UIBaselineAdjustmentNone: + break; + } + } + } + size.width = MIN(size.width, origSize.width); + // adjust the point for alignment + switch (self.textAlignment) { + case UITextAlignmentLeft: + break; + case UITextAlignmentCenter: + point.x += (origSize.width - size.width) / 2.0f; + break; + case UITextAlignmentRight: + point.x += origSize.width - size.width; + break; + } + [self.text drawAtPoint:point forWidth:size.width withZFont:actualFont lineBreakMode:self.lineBreakMode]; + } else { + CGSize size = [self.text sizeWithZFont:actualFont constrainedToSize:origSize lineBreakMode:self.lineBreakMode numberOfLines:self.numberOfLines]; + CGPoint point = rect.origin; + point.y += roundf((rect.size.height - size.height) / 2.0f); + rect = (CGRect){point, CGSizeMake(rect.size.width, size.height)}; + [self.text drawInRect:rect withZFont:actualFont lineBreakMode:self.lineBreakMode alignment:self.textAlignment numberOfLines:self.numberOfLines]; + } + } else { + ZAttributedString *attStr = self.zAttributedText; + if (self.highlighted) { + // modify the string to change the base color + ZMutableAttributedString *mutStr = [[attStr mutableCopy] autorelease]; + NSRange activeRange = NSMakeRange(0, attStr.length); + while (activeRange.length > 0) { + NSRange effective; + UIColor *color = [attStr attribute:ZForegroundColorAttributeName atIndex:activeRange.location + longestEffectiveRange:&effective inRange:activeRange]; + if (color == nil) { + [mutStr addAttribute:ZForegroundColorAttributeName value:[UIColor whiteColor] range:effective]; + } + activeRange.location += effective.length, activeRange.length -= effective.length; + } + attStr = mutStr; + } + CGSize size = [attStr sizeConstrainedToSize:rect.size lineBreakMode:self.lineBreakMode numberOfLines:self.numberOfLines]; + CGPoint point = rect.origin; + point.y += roundf((rect.size.height - size.height) / 2.0f); + rect = (CGRect){point, CGSizeMake(rect.size.width, size.height)}; + [attStr drawInRect:rect withLineBreakMode:self.lineBreakMode alignment:self.textAlignment numberOfLines:self.numberOfLines]; + } +} + +- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines { + if (self.zFont == NULL && self.zAttributedText == nil) { + return [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines]; + } + + if (numberOfLines == 1) { + // if numberOfLines == 1 we need to use the version that converts spaces + CGSize size; + if (self.zAttributedText == nil) { + size = [self.text sizeWithZFont:self.zFont]; + } else { + size = [self.zAttributedText size]; + } + bounds.size.width = MIN(bounds.size.width, size.width); + bounds.size.height = MIN(bounds.size.height, size.height); + } else { + if (numberOfLines > 0) bounds.size.height = MIN(bounds.size.height, self.zFont.leading * numberOfLines); + if (self.zAttributedText == nil) { + bounds.size = [self.text sizeWithZFont:self.zFont constrainedToSize:bounds.size lineBreakMode:self.lineBreakMode]; + } else { + bounds.size = [self.zAttributedText sizeConstrainedToSize:bounds.size lineBreakMode:self.lineBreakMode]; + } + } + return bounds; +} + +- (void)dealloc { + [zFont release]; + [zAttributedText release]; + [super dealloc]; +} +@end diff --git a/libs/FontLabel/FontLabelStringDrawing.h b/libs/FontLabel/FontLabelStringDrawing.h new file mode 100644 index 0000000..821da22 --- /dev/null +++ b/libs/FontLabel/FontLabelStringDrawing.h @@ -0,0 +1,69 @@ +// +// FontLabelStringDrawing.h +// FontLabel +// +// Created by Kevin Ballard on 5/5/09. +// Copyright © 2009 Zynga Game Networks +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "ZAttributedString.h" + +@class ZFont; + +@interface NSString (FontLabelStringDrawing) +// CGFontRef-based methods +- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize __AVAILABILITY_INTERNAL_DEPRECATED; +- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize constrainedToSize:(CGSize)size __AVAILABILITY_INTERNAL_DEPRECATED; +- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize constrainedToSize:(CGSize)size + lineBreakMode:(UILineBreakMode)lineBreakMode __AVAILABILITY_INTERNAL_DEPRECATED; +- (CGSize)drawAtPoint:(CGPoint)point withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize __AVAILABILITY_INTERNAL_DEPRECATED; +- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize __AVAILABILITY_INTERNAL_DEPRECATED; +- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize + lineBreakMode:(UILineBreakMode)lineBreakMode __AVAILABILITY_INTERNAL_DEPRECATED; +- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize + lineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment __AVAILABILITY_INTERNAL_DEPRECATED; + +// ZFont-based methods +- (CGSize)sizeWithZFont:(ZFont *)font; +- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size; +- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode; +- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode + numberOfLines:(NSUInteger)numberOfLines; +- (CGSize)drawAtPoint:(CGPoint)point withZFont:(ZFont *)font; +- (CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode; +- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font; +- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode; +- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode + alignment:(UITextAlignment)alignment; +- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode + alignment:(UITextAlignment)alignment numberOfLines:(NSUInteger)numberOfLines; +@end + +@interface ZAttributedString (ZAttributedStringDrawing) +- (CGSize)size; +- (CGSize)sizeConstrainedToSize:(CGSize)size; +- (CGSize)sizeConstrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode; +- (CGSize)sizeConstrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode + numberOfLines:(NSUInteger)numberOfLines; +- (CGSize)drawAtPoint:(CGPoint)point; +- (CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode; +- (CGSize)drawInRect:(CGRect)rect; +- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode; +- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment; +- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment + numberOfLines:(NSUInteger)numberOfLines; +@end diff --git a/libs/FontLabel/FontLabelStringDrawing.m b/libs/FontLabel/FontLabelStringDrawing.m new file mode 100644 index 0000000..2907372 --- /dev/null +++ b/libs/FontLabel/FontLabelStringDrawing.m @@ -0,0 +1,892 @@ +// +// FontLabelStringDrawing.m +// FontLabel +// +// Created by Kevin Ballard on 5/5/09. +// Copyright © 2009 Zynga Game Networks +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "FontLabelStringDrawing.h" +#import "ZFont.h" +#import "ZAttributedStringPrivate.h" + +@interface ZFont (ZFontPrivate) +@property (nonatomic, readonly) CGFloat ratio; +@end + +#define kUnicodeHighSurrogateStart 0xD800 +#define kUnicodeHighSurrogateEnd 0xDBFF +#define kUnicodeHighSurrogateMask kUnicodeHighSurrogateStart +#define kUnicodeLowSurrogateStart 0xDC00 +#define kUnicodeLowSurrogateEnd 0xDFFF +#define kUnicodeLowSurrogateMask kUnicodeLowSurrogateStart +#define kUnicodeSurrogateTypeMask 0xFC00 +#define UnicharIsHighSurrogate(c) ((c & kUnicodeSurrogateTypeMask) == kUnicodeHighSurrogateMask) +#define UnicharIsLowSurrogate(c) ((c & kUnicodeSurrogateTypeMask) == kUnicodeLowSurrogateMask) +#define ConvertSurrogatePairToUTF32(high, low) ((UInt32)((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000)) + +typedef enum { + kFontTableFormat4 = 4, + kFontTableFormat12 = 12, +} FontTableFormat; + +typedef struct fontTable { + NSUInteger retainCount; + CFDataRef cmapTable; + FontTableFormat format; + union { + struct { + UInt16 segCountX2; + UInt16 *endCodes; + UInt16 *startCodes; + UInt16 *idDeltas; + UInt16 *idRangeOffsets; + } format4; + struct { + UInt32 nGroups; + struct { + UInt32 startCharCode; + UInt32 endCharCode; + UInt32 startGlyphCode; + } *groups; + } format12; + } cmap; +} fontTable; + +static FontTableFormat supportedFormats[] = { kFontTableFormat4, kFontTableFormat12 }; +static size_t supportedFormatsCount = sizeof(supportedFormats) / sizeof(FontTableFormat); + +static fontTable *newFontTable(CFDataRef cmapTable, FontTableFormat format) { + fontTable *table = (struct fontTable *)malloc(sizeof(struct fontTable)); + table->retainCount = 1; + table->cmapTable = CFRetain(cmapTable); + table->format = format; + return table; +} + +static fontTable *retainFontTable(fontTable *table) { + if (table != NULL) { + table->retainCount++; + } + return table; +} + +static void releaseFontTable(fontTable *table) { + if (table != NULL) { + if (table->retainCount <= 1) { + CFRelease(table->cmapTable); + free(table); + } else { + table->retainCount--; + } + } +} + +static const void *fontTableRetainCallback(CFAllocatorRef allocator, const void *value) { + return retainFontTable((fontTable *)value); +} + +static void fontTableReleaseCallback(CFAllocatorRef allocator, const void *value) { + releaseFontTable((fontTable *)value); +} + +static const CFDictionaryValueCallBacks kFontTableDictionaryValueCallBacks = { + .version = 0, + .retain = &fontTableRetainCallback, + .release = &fontTableReleaseCallback, + .copyDescription = NULL, + .equal = NULL +}; + +// read the cmap table from the font +// we only know how to understand some of the table formats at the moment +static fontTable *readFontTableFromCGFont(CGFontRef font) { + CFDataRef cmapTable = CGFontCopyTableForTag(font, 'cmap'); + NSCAssert1(cmapTable != NULL, @"CGFontCopyTableForTag returned NULL for 'cmap' tag in font %@", + (font ? [(id)CFCopyDescription(font) autorelease] : @"(null)")); + const UInt8 * const bytes = CFDataGetBytePtr(cmapTable); + NSCAssert1(OSReadBigInt16(bytes, 0) == 0, @"cmap table for font %@ has bad version number", + (font ? [(id)CFCopyDescription(font) autorelease] : @"(null)")); + UInt16 numberOfSubtables = OSReadBigInt16(bytes, 2); + const UInt8 *unicodeSubtable = NULL; + //UInt16 unicodeSubtablePlatformID; + UInt16 unicodeSubtablePlatformSpecificID; + FontTableFormat unicodeSubtableFormat; + const UInt8 * const encodingSubtables = &bytes[4]; + for (UInt16 i = 0; i < numberOfSubtables; i++) { + const UInt8 * const encodingSubtable = &encodingSubtables[8 * i]; + UInt16 platformID = OSReadBigInt16(encodingSubtable, 0); + UInt16 platformSpecificID = OSReadBigInt16(encodingSubtable, 2); + // find the best subtable + // best is defined by a combination of encoding and format + // At the moment we only support format 4, so ignore all other format tables + // We prefer platformID == 0, but we will also accept Microsoft's unicode format + if (platformID == 0 || (platformID == 3 && platformSpecificID == 1)) { + BOOL preferred = NO; + if (unicodeSubtable == NULL) { + preferred = YES; + } else if (platformID == 0 && platformSpecificID > unicodeSubtablePlatformSpecificID) { + preferred = YES; + } + if (preferred) { + UInt32 offset = OSReadBigInt32(encodingSubtable, 4); + const UInt8 *subtable = &bytes[offset]; + UInt16 format = OSReadBigInt16(subtable, 0); + for (size_t i = 0; i < supportedFormatsCount; i++) { + if (format == supportedFormats[i]) { + if (format >= 8) { + // the version is a fixed-point + UInt16 formatFrac = OSReadBigInt16(subtable, 2); + if (formatFrac != 0) { + // all the current formats with a Fixed version are always *.0 + continue; + } + } + unicodeSubtable = subtable; + //unicodeSubtablePlatformID = platformID; + unicodeSubtablePlatformSpecificID = platformSpecificID; + unicodeSubtableFormat = format; + break; + } + } + } + } + } + fontTable *table = NULL; + if (unicodeSubtable != NULL) { + table = newFontTable(cmapTable, unicodeSubtableFormat); + switch (unicodeSubtableFormat) { + case kFontTableFormat4: + // subtable format 4 + //UInt16 length = OSReadBigInt16(unicodeSubtable, 2); + //UInt16 language = OSReadBigInt16(unicodeSubtable, 4); + table->cmap.format4.segCountX2 = OSReadBigInt16(unicodeSubtable, 6); + //UInt16 searchRange = OSReadBigInt16(unicodeSubtable, 8); + //UInt16 entrySelector = OSReadBigInt16(unicodeSubtable, 10); + //UInt16 rangeShift = OSReadBigInt16(unicodeSubtable, 12); + table->cmap.format4.endCodes = (UInt16*)&unicodeSubtable[14]; + table->cmap.format4.startCodes = (UInt16*)&((UInt8*)table->cmap.format4.endCodes)[table->cmap.format4.segCountX2+2]; + table->cmap.format4.idDeltas = (UInt16*)&((UInt8*)table->cmap.format4.startCodes)[table->cmap.format4.segCountX2]; + table->cmap.format4.idRangeOffsets = (UInt16*)&((UInt8*)table->cmap.format4.idDeltas)[table->cmap.format4.segCountX2]; + //UInt16 *glyphIndexArray = &idRangeOffsets[segCountX2]; + break; + case kFontTableFormat12: + table->cmap.format12.nGroups = OSReadBigInt32(unicodeSubtable, 12); + table->cmap.format12.groups = (void *)&unicodeSubtable[16]; + break; + default: + releaseFontTable(table); + table = NULL; + } + } + CFRelease(cmapTable); + return table; +} + +// outGlyphs must be at least size n +static void mapCharactersToGlyphsInFont(const fontTable *table, unichar characters[], size_t charLen, CGGlyph outGlyphs[], size_t *outGlyphLen) { + if (table != NULL) { + NSUInteger j = 0; + switch (table->format) { + case kFontTableFormat4: { + for (NSUInteger i = 0; i < charLen; i++, j++) { + unichar c = characters[i]; + UInt16 segOffset; + BOOL foundSegment = NO; + for (segOffset = 0; segOffset < table->cmap.format4.segCountX2; segOffset += 2) { + UInt16 endCode = OSReadBigInt16(table->cmap.format4.endCodes, segOffset); + if (endCode >= c) { + foundSegment = YES; + break; + } + } + if (!foundSegment) { + // no segment + // this is an invalid font + outGlyphs[j] = 0; + } else { + UInt16 startCode = OSReadBigInt16(table->cmap.format4.startCodes, segOffset); + if (!(startCode <= c)) { + // the code falls in a hole between segments + outGlyphs[j] = 0; + } else { + UInt16 idRangeOffset = OSReadBigInt16(table->cmap.format4.idRangeOffsets, segOffset); + if (idRangeOffset == 0) { + UInt16 idDelta = OSReadBigInt16(table->cmap.format4.idDeltas, segOffset); + outGlyphs[j] = (c + idDelta) % 65536; + } else { + // use the glyphIndexArray + UInt16 glyphOffset = idRangeOffset + 2 * (c - startCode); + outGlyphs[j] = OSReadBigInt16(&((UInt8*)table->cmap.format4.idRangeOffsets)[segOffset], glyphOffset); + } + } + } + } + break; + } + case kFontTableFormat12: { + UInt32 lastSegment = UINT32_MAX; + for (NSUInteger i = 0; i < charLen; i++, j++) { + unichar c = characters[i]; + UInt32 c32 = c; + if (UnicharIsHighSurrogate(c)) { + if (i+1 < charLen) { // do we have another character after this one? + unichar cc = characters[i+1]; + if (UnicharIsLowSurrogate(cc)) { + c32 = ConvertSurrogatePairToUTF32(c, cc); + i++; + } + } + } + // Start the heuristic search + // If this is an ASCII char, just do a linear search + // Otherwise do a hinted, modified binary search + // Start the first pivot at the last range found + // And when moving the pivot, limit the movement by increasing + // powers of two. This should help with locality + __typeof__(table->cmap.format12.groups[0]) *foundGroup = NULL; + if (c32 <= 0x7F) { + // ASCII + for (UInt32 idx = 0; idx < table->cmap.format12.nGroups; idx++) { + __typeof__(table->cmap.format12.groups[idx]) *group = &table->cmap.format12.groups[idx]; + if (c32 < OSSwapBigToHostInt32(group->startCharCode)) { + // we've fallen into a hole + break; + } else if (c32 <= OSSwapBigToHostInt32(group->endCharCode)) { + // this is the range + foundGroup = group; + break; + } + } + } else { + // heuristic search + UInt32 maxJump = (lastSegment == UINT32_MAX ? UINT32_MAX / 2 : 8); + UInt32 lowIdx = 0, highIdx = table->cmap.format12.nGroups; // highIdx is the first invalid idx + UInt32 pivot = (lastSegment == UINT32_MAX ? lowIdx + (highIdx - lowIdx) / 2 : lastSegment); + while (highIdx > lowIdx) { + __typeof__(table->cmap.format12.groups[pivot]) *group = &table->cmap.format12.groups[pivot]; + if (c32 < OSSwapBigToHostInt32(group->startCharCode)) { + highIdx = pivot; + } else if (c32 > OSSwapBigToHostInt32(group->endCharCode)) { + lowIdx = pivot + 1; + } else { + // we've hit the range + foundGroup = group; + break; + } + if (highIdx - lowIdx > maxJump * 2) { + if (highIdx == pivot) { + pivot -= maxJump; + } else { + pivot += maxJump; + } + maxJump *= 2; + } else { + pivot = lowIdx + (highIdx - lowIdx) / 2; + } + } + if (foundGroup != NULL) lastSegment = pivot; + } + if (foundGroup == NULL) { + outGlyphs[j] = 0; + } else { + outGlyphs[j] = (CGGlyph)(OSSwapBigToHostInt32(foundGroup->startGlyphCode) + + (c32 - OSSwapBigToHostInt32(foundGroup->startCharCode))); + } + } + break; + } + } + if (outGlyphLen != NULL) *outGlyphLen = j; + } else { + // we have no table, so just null out the glyphs + bzero(outGlyphs, charLen*sizeof(CGGlyph)); + if (outGlyphLen != NULL) *outGlyphLen = 0; + } +} + +static BOOL mapGlyphsToAdvancesInFont(ZFont *font, size_t n, CGGlyph glyphs[], CGFloat outAdvances[]) { + int advances[n]; + if (CGFontGetGlyphAdvances(font.cgFont, glyphs, n, advances)) { + CGFloat ratio = font.ratio; + + for (size_t i = 0; i < n; i++) { + outAdvances[i] = advances[i]*ratio; + } + return YES; + } else { + bzero(outAdvances, n*sizeof(CGFloat)); + } + return NO; +} + +static id getValueOrDefaultForRun(ZAttributeRun *run, NSString *key) { + id value = [run.attributes objectForKey:key]; + if (value == nil) { + static NSDictionary *defaultValues = nil; + if (defaultValues == nil) { + defaultValues = [[NSDictionary alloc] initWithObjectsAndKeys: + [ZFont fontWithUIFont:[UIFont systemFontOfSize:12]], ZFontAttributeName, + [UIColor blackColor], ZForegroundColorAttributeName, + [UIColor clearColor], ZBackgroundColorAttributeName, + [NSNumber numberWithInt:ZUnderlineStyleNone], ZUnderlineStyleAttributeName, + nil]; + } + value = [defaultValues objectForKey:key]; + } + return value; +} + +static void readRunInformation(NSArray *attributes, NSUInteger len, CFMutableDictionaryRef fontTableMap, + NSUInteger index, ZAttributeRun **currentRun, NSUInteger *nextRunStart, + ZFont **currentFont, fontTable **currentTable) { + *currentRun = [attributes objectAtIndex:index]; + *nextRunStart = ([attributes count] > index+1 ? [[attributes objectAtIndex:index+1] index] : len); + *currentFont = getValueOrDefaultForRun(*currentRun, ZFontAttributeName); + if (!CFDictionaryGetValueIfPresent(fontTableMap, (*currentFont).cgFont, (const void **)currentTable)) { + *currentTable = readFontTableFromCGFont((*currentFont).cgFont); + CFDictionarySetValue(fontTableMap, (*currentFont).cgFont, *currentTable); + releaseFontTable(*currentTable); + } +} + +static CGSize drawOrSizeTextConstrainedToSize(BOOL performDraw, NSString *string, NSArray *attributes, CGSize constrainedSize, NSUInteger maxLines, + UILineBreakMode lineBreakMode, UITextAlignment alignment, BOOL ignoreColor) { + NSUInteger len = [string length]; + NSUInteger idx = 0; + CGPoint drawPoint = CGPointZero; + CGSize retValue = CGSizeZero; + CGContextRef ctx = (performDraw ? UIGraphicsGetCurrentContext() : NULL); + + BOOL convertNewlines = (maxLines == 1); + + // Extract the characters from the string + // Convert newlines to spaces if necessary + unichar *characters = (unichar *)malloc(sizeof(unichar) * len); + if (convertNewlines) { + NSCharacterSet *charset = [NSCharacterSet newlineCharacterSet]; + NSRange range = NSMakeRange(0, len); + size_t cIdx = 0; + while (range.length > 0) { + NSRange newlineRange = [string rangeOfCharacterFromSet:charset options:0 range:range]; + if (newlineRange.location == NSNotFound) { + [string getCharacters:&characters[cIdx] range:range]; + cIdx += range.length; + break; + } else { + NSUInteger delta = newlineRange.location - range.location; + if (newlineRange.location > range.location) { + [string getCharacters:&characters[cIdx] range:NSMakeRange(range.location, delta)]; + } + cIdx += delta; + characters[cIdx] = (unichar)' '; + cIdx++; + delta += newlineRange.length; + range.location += delta, range.length -= delta; + if (newlineRange.length == 1 && range.length >= 1 && + [string characterAtIndex:newlineRange.location] == (unichar)'\r' && + [string characterAtIndex:range.location] == (unichar)'\n') { + // CRLF sequence, skip the LF + range.location += 1, range.length -= 1; + } + } + } + len = cIdx; + } else { + [string getCharacters:characters range:NSMakeRange(0, len)]; + } + + // Create storage for glyphs and advances + CGGlyph *glyphs; + CGFloat *advances; + { + NSUInteger maxRunLength = 0; + ZAttributeRun *a = [attributes objectAtIndex:0]; + for (NSUInteger i = 1; i < [attributes count]; i++) { + ZAttributeRun *b = [attributes objectAtIndex:i]; + maxRunLength = MAX(maxRunLength, b.index - a.index); + a = b; + } + maxRunLength = MAX(maxRunLength, len - a.index); + maxRunLength++; // for a potential ellipsis + glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * maxRunLength); + advances = (CGFloat *)malloc(sizeof(CGFloat) * maxRunLength); + } + + // Use this table to cache all fontTable objects + CFMutableDictionaryRef fontTableMap = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, + &kFontTableDictionaryValueCallBacks); + + // Fetch initial style values + NSUInteger currentRunIdx = 0; + ZAttributeRun *currentRun; + NSUInteger nextRunStart; + ZFont *currentFont; + fontTable *currentTable; + +#define READ_RUN() readRunInformation(attributes, len, fontTableMap, \ + currentRunIdx, ¤tRun, &nextRunStart, \ + ¤tFont, ¤tTable) + + READ_RUN(); + + // fetch the glyphs for the first run + size_t glyphCount; + NSUInteger glyphIdx; + +#define READ_GLYPHS() do { \ + mapCharactersToGlyphsInFont(currentTable, &characters[currentRun.index], (nextRunStart - currentRun.index), glyphs, &glyphCount); \ + mapGlyphsToAdvancesInFont(currentFont, (nextRunStart - currentRun.index), glyphs, advances); \ + glyphIdx = 0; \ + } while (0) + + READ_GLYPHS(); + + NSMutableCharacterSet *alphaCharset = [NSMutableCharacterSet alphanumericCharacterSet]; + [alphaCharset addCharactersInString:@"([{'\"\u2019\u02BC"]; + + // scan left-to-right looking for newlines or until we hit the width constraint + // When we hit a wrapping point, calculate truncation as follows: + // If we have room to draw at least one more character on the next line, no truncation + // Otherwise apply the truncation algorithm to the current line. + // After calculating any truncation, draw. + // Each time we hit the end of an attribute run, calculate the new font and make sure + // it fits (vertically) within the size constraint. If not, truncate this line. + // When we draw, iterate over the attribute runs for this line and draw each run separately + BOOL lastLine = NO; // used to indicate truncation and to stop the iterating + NSUInteger lineCount = 1; + while (idx < len && !lastLine) { + if (maxLines > 0 && lineCount == maxLines) { + lastLine = YES; + } + // scan left-to-right + struct { + NSUInteger index; + NSUInteger glyphIndex; + NSUInteger currentRunIdx; + } indexCache = { idx, glyphIdx, currentRunIdx }; + CGSize lineSize = CGSizeMake(0, currentFont.leading); + CGFloat lineAscender = currentFont.ascender; + struct { + NSUInteger index; + NSUInteger glyphIndex; + NSUInteger currentRunIdx; + CGSize lineSize; + } lastWrapCache = {0, 0, 0, CGSizeZero}; + BOOL inAlpha = NO; // used for calculating wrap points + + BOOL finishLine = NO; + for (;idx <= len && !finishLine;) { + NSUInteger skipCount = 0; + if (idx == len) { + finishLine = YES; + lastLine = YES; + } else { + if (idx >= nextRunStart) { + // cycle the font and table and grab the next set of glyphs + do { + currentRunIdx++; + READ_RUN(); + } while (idx >= nextRunStart); + READ_GLYPHS(); + // re-scan the characters to synchronize the glyph index + for (NSUInteger j = currentRun.index; j < idx; j++) { + if (UnicharIsHighSurrogate(characters[j]) && j+1 lineSize.height) { + lineSize.height = currentFont.leading; + if (retValue.height + currentFont.ascender > constrainedSize.height) { + lastLine = YES; + finishLine = YES; + } + } + lineAscender = MAX(lineAscender, currentFont.ascender); + } + unichar c = characters[idx]; + // Mark a wrap point before spaces and after any stretch of non-alpha characters + BOOL markWrap = NO; + if (c == (unichar)' ') { + markWrap = YES; + } else if ([alphaCharset characterIsMember:c]) { + if (!inAlpha) { + markWrap = YES; + inAlpha = YES; + } + } else { + inAlpha = NO; + } + if (markWrap) { + lastWrapCache = (__typeof__(lastWrapCache)){ + .index = idx, + .glyphIndex = glyphIdx, + .currentRunIdx = currentRunIdx, + .lineSize = lineSize + }; + } + // process the line + if (c == (unichar)'\n' || c == 0x0085) { // U+0085 is the NEXT_LINE unicode character + finishLine = YES; + skipCount = 1; + } else if (c == (unichar)'\r') { + finishLine = YES; + // check for CRLF + if (idx+1 < len && characters[idx+1] == (unichar)'\n') { + skipCount = 2; + } else { + skipCount = 1; + } + } else if (lineSize.width + advances[glyphIdx] > constrainedSize.width) { + finishLine = YES; + if (retValue.height + lineSize.height + currentFont.ascender > constrainedSize.height) { + lastLine = YES; + } + // walk backwards if wrapping is necessary + if (lastWrapCache.index > indexCache.index && lineBreakMode != UILineBreakModeCharacterWrap && + (!lastLine || lineBreakMode != UILineBreakModeClip)) { + // we're doing some sort of word wrapping + idx = lastWrapCache.index; + lineSize = lastWrapCache.lineSize; + if (!lastLine) { + // re-check if this is the last line + if (lastWrapCache.currentRunIdx != currentRunIdx) { + currentRunIdx = lastWrapCache.currentRunIdx; + READ_RUN(); + READ_GLYPHS(); + } + if (retValue.height + lineSize.height + currentFont.ascender > constrainedSize.height) { + lastLine = YES; + } + } + glyphIdx = lastWrapCache.glyphIndex; + // skip any spaces + for (NSUInteger j = idx; j < len && characters[j] == (unichar)' '; j++) { + skipCount++; + } + } + } + } + if (finishLine) { + // TODO: support head/middle truncation + if (lastLine && idx < len && lineBreakMode == UILineBreakModeTailTruncation) { + // truncate + unichar ellipsis = 0x2026; // ellipsis (…) + CGGlyph ellipsisGlyph; + mapCharactersToGlyphsInFont(currentTable, &ellipsis, 1, &ellipsisGlyph, NULL); + CGFloat ellipsisWidth; + mapGlyphsToAdvancesInFont(currentFont, 1, &ellipsisGlyph, &ellipsisWidth); + while ((idx - indexCache.index) > 1 && lineSize.width + ellipsisWidth > constrainedSize.width) { + // we have more than 1 character and we're too wide, so back up + idx--; + if (UnicharIsHighSurrogate(characters[idx]) && UnicharIsLowSurrogate(characters[idx+1])) { + idx--; + } + if (idx < currentRun.index) { + ZFont *oldFont = currentFont; + do { + currentRunIdx--; + READ_RUN(); + } while (idx < currentRun.index); + READ_GLYPHS(); + glyphIdx = glyphCount-1; + if (oldFont != currentFont) { + mapCharactersToGlyphsInFont(currentTable, &ellipsis, 1, &ellipsisGlyph, NULL); + mapGlyphsToAdvancesInFont(currentFont, 1, &ellipsisGlyph, &ellipsisWidth); + } + } else { + glyphIdx--; + } + lineSize.width -= advances[glyphIdx]; + } + // skip any spaces before truncating + while ((idx - indexCache.index) > 1 && characters[idx-1] == (unichar)' ') { + idx--; + if (idx < currentRun.index) { + currentRunIdx--; + READ_RUN(); + READ_GLYPHS(); + glyphIdx = glyphCount-1; + } else { + glyphIdx--; + } + lineSize.width -= advances[glyphIdx]; + } + lineSize.width += ellipsisWidth; + glyphs[glyphIdx] = ellipsisGlyph; + idx++; + glyphIdx++; + } + retValue.width = MAX(retValue.width, lineSize.width); + retValue.height += lineSize.height; + + // draw + if (performDraw) { + switch (alignment) { + case UITextAlignmentLeft: + drawPoint.x = 0; + break; + case UITextAlignmentCenter: + drawPoint.x = (constrainedSize.width - lineSize.width) / 2.0f; + break; + case UITextAlignmentRight: + drawPoint.x = constrainedSize.width - lineSize.width; + break; + } + NSUInteger stopGlyphIdx = glyphIdx; + NSUInteger lastRunIdx = currentRunIdx; + NSUInteger stopCharIdx = idx; + idx = indexCache.index; + if (currentRunIdx != indexCache.currentRunIdx) { + currentRunIdx = indexCache.currentRunIdx; + READ_RUN(); + READ_GLYPHS(); + } + glyphIdx = indexCache.glyphIndex; + for (NSUInteger drawIdx = currentRunIdx; drawIdx <= lastRunIdx; drawIdx++) { + if (drawIdx != currentRunIdx) { + currentRunIdx = drawIdx; + READ_RUN(); + READ_GLYPHS(); + } + NSUInteger numGlyphs; + if (drawIdx == lastRunIdx) { + numGlyphs = stopGlyphIdx - glyphIdx; + idx = stopCharIdx; + } else { + numGlyphs = glyphCount - glyphIdx; + idx = nextRunStart; + } + CGContextSetFont(ctx, currentFont.cgFont); + CGContextSetFontSize(ctx, currentFont.pointSize); + // calculate the fragment size + CGFloat fragmentWidth = 0; + for (NSUInteger g = 0; g < numGlyphs; g++) { + fragmentWidth += advances[glyphIdx + g]; + } + + if (!ignoreColor) { + UIColor *foregroundColor = getValueOrDefaultForRun(currentRun, ZForegroundColorAttributeName); + UIColor *backgroundColor = getValueOrDefaultForRun(currentRun, ZBackgroundColorAttributeName); + if (backgroundColor != nil && ![backgroundColor isEqual:[UIColor clearColor]]) { + [backgroundColor setFill]; + UIRectFillUsingBlendMode((CGRect){ drawPoint, { fragmentWidth, lineSize.height } }, kCGBlendModeNormal); + } + [foregroundColor setFill]; + } + + CGContextShowGlyphsAtPoint(ctx, drawPoint.x, drawPoint.y + lineAscender, &glyphs[glyphIdx], numGlyphs); + NSNumber *underlineStyle = getValueOrDefaultForRun(currentRun, ZUnderlineStyleAttributeName); + if ([underlineStyle integerValue] & ZUnderlineStyleMask) { + // we only support single for the time being + UIRectFill(CGRectMake(drawPoint.x, drawPoint.y + lineAscender, fragmentWidth, 1)); + } + drawPoint.x += fragmentWidth; + glyphIdx += numGlyphs; + } + drawPoint.y += lineSize.height; + } + idx += skipCount; + glyphIdx += skipCount; + lineCount++; + } else { + lineSize.width += advances[glyphIdx]; + glyphIdx++; + idx++; + if (idx < len && UnicharIsHighSurrogate(characters[idx-1]) && UnicharIsLowSurrogate(characters[idx])) { + // skip the second half of the surrogate pair + idx++; + } + } + } + } + CFRelease(fontTableMap); + free(glyphs); + free(advances); + free(characters); + +#undef READ_GLYPHS +#undef READ_RUN + + return retValue; +} + +static NSArray *attributeRunForFont(ZFont *font) { + return [NSArray arrayWithObject:[ZAttributeRun attributeRunWithIndex:0 + attributes:[NSDictionary dictionaryWithObject:font + forKey:ZFontAttributeName]]]; +} + +static CGSize drawTextInRect(CGRect rect, NSString *text, NSArray *attributes, UILineBreakMode lineBreakMode, + UITextAlignment alignment, NSUInteger numberOfLines, BOOL ignoreColor) { + CGContextRef ctx = UIGraphicsGetCurrentContext(); + + CGContextSaveGState(ctx); + + // flip it upside-down because our 0,0 is upper-left, whereas ttfs are for screens where 0,0 is lower-left + CGAffineTransform textTransform = CGAffineTransformMake(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f); + CGContextSetTextMatrix(ctx, textTransform); + + CGContextTranslateCTM(ctx, rect.origin.x, rect.origin.y); + + CGContextSetTextDrawingMode(ctx, kCGTextFill); + CGSize size = drawOrSizeTextConstrainedToSize(YES, text, attributes, rect.size, numberOfLines, lineBreakMode, alignment, ignoreColor); + + CGContextRestoreGState(ctx); + + return size; +} + +@implementation NSString (FontLabelStringDrawing) +// CGFontRef-based methods +- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize { + return [self sizeWithZFont:[ZFont fontWithCGFont:font size:pointSize]]; +} + +- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize constrainedToSize:(CGSize)size { + return [self sizeWithZFont:[ZFont fontWithCGFont:font size:pointSize] constrainedToSize:size]; +} + +- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize constrainedToSize:(CGSize)size + lineBreakMode:(UILineBreakMode)lineBreakMode { + return [self sizeWithZFont:[ZFont fontWithCGFont:font size:pointSize] constrainedToSize:size lineBreakMode:lineBreakMode]; +} + +- (CGSize)drawAtPoint:(CGPoint)point withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize { + return [self drawAtPoint:point withZFont:[ZFont fontWithCGFont:font size:pointSize]]; +} + +- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize { + return [self drawInRect:rect withZFont:[ZFont fontWithCGFont:font size:pointSize]]; +} + +- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize lineBreakMode:(UILineBreakMode)lineBreakMode { + return [self drawInRect:rect withZFont:[ZFont fontWithCGFont:font size:pointSize] lineBreakMode:lineBreakMode]; +} + +- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize + lineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment { + return [self drawInRect:rect withZFont:[ZFont fontWithCGFont:font size:pointSize] lineBreakMode:lineBreakMode alignment:alignment]; +} + +// ZFont-based methods +- (CGSize)sizeWithZFont:(ZFont *)font { + CGSize size = drawOrSizeTextConstrainedToSize(NO, self, attributeRunForFont(font), CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX), 1, + UILineBreakModeClip, UITextAlignmentLeft, YES); + return CGSizeMake(ceilf(size.width), ceilf(size.height)); +} + +- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size { + return [self sizeWithZFont:font constrainedToSize:size lineBreakMode:UILineBreakModeWordWrap]; +} + +/* + According to experimentation with UIStringDrawing, this can actually return a CGSize whose height is greater + than the one passed in. The two cases are as follows: + 1. If the given size parameter's height is smaller than a single line, the returned value will + be the height of one line. + 2. If the given size parameter's height falls between multiples of a line height, and the wrapped string + actually extends past the size.height, and the difference between size.height and the previous multiple + of a line height is >= the font's ascender, then the returned size's height is extended to the next line. + To put it simply, if the baseline point of a given line falls in the given size, the entire line will + be present in the output size. + */ +- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode { + size = drawOrSizeTextConstrainedToSize(NO, self, attributeRunForFont(font), size, 0, lineBreakMode, UITextAlignmentLeft, YES); + return CGSizeMake(ceilf(size.width), ceilf(size.height)); +} + +- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode + numberOfLines:(NSUInteger)numberOfLines { + size = drawOrSizeTextConstrainedToSize(NO, self, attributeRunForFont(font), size, numberOfLines, lineBreakMode, UITextAlignmentLeft, YES); + return CGSizeMake(ceilf(size.width), ceilf(size.height)); +} + +- (CGSize)drawAtPoint:(CGPoint)point withZFont:(ZFont *)font { + return [self drawAtPoint:point forWidth:CGFLOAT_MAX withZFont:font lineBreakMode:UILineBreakModeClip]; +} + +- (CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode { + return drawTextInRect((CGRect){ point, { width, CGFLOAT_MAX } }, self, attributeRunForFont(font), lineBreakMode, UITextAlignmentLeft, 1, YES); +} + +- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font { + return [self drawInRect:rect withZFont:font lineBreakMode:UILineBreakModeWordWrap]; +} + +- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode { + return [self drawInRect:rect withZFont:font lineBreakMode:lineBreakMode alignment:UITextAlignmentLeft]; +} + +- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode + alignment:(UITextAlignment)alignment { + return drawTextInRect(rect, self, attributeRunForFont(font), lineBreakMode, alignment, 0, YES); +} + +- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode + alignment:(UITextAlignment)alignment numberOfLines:(NSUInteger)numberOfLines { + return drawTextInRect(rect, self, attributeRunForFont(font), lineBreakMode, alignment, numberOfLines, YES); +} +@end + +@implementation ZAttributedString (ZAttributedStringDrawing) +- (CGSize)size { + CGSize size = drawOrSizeTextConstrainedToSize(NO, self.string, self.attributes, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX), 1, + UILineBreakModeClip, UITextAlignmentLeft, NO); + return CGSizeMake(ceilf(size.width), ceilf(size.height)); +} + +- (CGSize)sizeConstrainedToSize:(CGSize)size { + return [self sizeConstrainedToSize:size lineBreakMode:UILineBreakModeWordWrap]; +} + +- (CGSize)sizeConstrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode { + size = drawOrSizeTextConstrainedToSize(NO, self.string, self.attributes, size, 0, lineBreakMode, UITextAlignmentLeft, NO); + return CGSizeMake(ceilf(size.width), ceilf(size.height)); +} + +- (CGSize)sizeConstrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode + numberOfLines:(NSUInteger)numberOfLines { + size = drawOrSizeTextConstrainedToSize(NO, self.string, self.attributes, size, numberOfLines, lineBreakMode, UITextAlignmentLeft, NO); + return CGSizeMake(ceilf(size.width), ceilf(size.height)); +} + +- (CGSize)drawAtPoint:(CGPoint)point { + return [self drawAtPoint:point forWidth:CGFLOAT_MAX lineBreakMode:UILineBreakModeClip]; +} + +- (CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode { + return drawTextInRect((CGRect){ point, { width, CGFLOAT_MAX } }, self.string, self.attributes, lineBreakMode, UITextAlignmentLeft, 1, NO); +} + +- (CGSize)drawInRect:(CGRect)rect { + return [self drawInRect:rect withLineBreakMode:UILineBreakModeWordWrap]; +} + +- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode { + return [self drawInRect:rect withLineBreakMode:lineBreakMode alignment:UITextAlignmentLeft]; +} + +- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment { + return drawTextInRect(rect, self.string, self.attributes, lineBreakMode, alignment, 0, NO); +} + +- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment + numberOfLines:(NSUInteger)numberOfLines { + return drawTextInRect(rect, self.string, self.attributes, lineBreakMode, alignment, numberOfLines, NO); +} +@end diff --git a/libs/FontLabel/FontManager.h b/libs/FontLabel/FontManager.h new file mode 100644 index 0000000..1592b8a --- /dev/null +++ b/libs/FontLabel/FontManager.h @@ -0,0 +1,85 @@ +// +// FontManager.h +// FontLabel +// +// Created by Kevin Ballard on 5/5/09. +// Copyright © 2009 Zynga Game Networks +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import + +@class ZFont; + +@interface FontManager : NSObject { + CFMutableDictionaryRef fonts; + NSMutableDictionary *urls; +} ++ (FontManager *)sharedManager; +/*! + @method + @abstract Loads a TTF font from the main bundle + @param filename The name of the font file to load (with or without extension). + @return YES if the font was loaded, NO if an error occurred + @discussion If the font has already been loaded, this method does nothing and returns YES. + This method first attempts to load the font by appending .ttf to the filename. + If that file does not exist, it tries the filename exactly as given. +*/ +- (BOOL)loadFont:(NSString *)filename; +/*! + @method + @abstract Loads a font from the given file URL + @param url A file URL that points to a font file + @return YES if the font was loaded, NO if an error occurred + @discussion If the font has already been loaded, this method does nothing and returns YES. +*/ +- (BOOL)loadFontURL:(NSURL *)url; +/*! + @method + @abstract Returns the loaded font with the given filename + @param filename The name of the font file that was given to -loadFont: + @return A CGFontRef, or NULL if the specified font cannot be found + @discussion If the font has not been loaded yet, -loadFont: will be + called with the given name first. +*/ +- (CGFontRef)fontWithName:(NSString *)filename __AVAILABILITY_INTERNAL_DEPRECATED; +/*! + @method + @abstract Returns a ZFont object corresponding to the loaded font with the given filename and point size + @param filename The name of the font file that was given to -loadFont: + @param pointSize The point size of the font + @return A ZFont, or NULL if the specified font cannot be found + @discussion If the font has not been loaded yet, -loadFont: will be + called with the given name first. +*/ +- (ZFont *)zFontWithName:(NSString *)filename pointSize:(CGFloat)pointSize; +/*! + @method + @abstract Returns a ZFont object corresponding to the loaded font with the given file URL and point size + @param url A file URL that points to a font file + @param pointSize The point size of the font + @return A ZFont, or NULL if the specified font cannot be loaded + @discussion If the font has not been loaded yet, -loadFontURL: will be called with the given URL first. +*/ +- (ZFont *)zFontWithURL:(NSURL *)url pointSize:(CGFloat)pointSize; +/*! + @method + @abstract Returns a CFArrayRef of all loaded CGFont objects + @return A CFArrayRef of all loaded CGFont objects + @description You are responsible for releasing the CFArrayRef +*/ +- (CFArrayRef)copyAllFonts; +@end diff --git a/libs/FontLabel/FontManager.m b/libs/FontLabel/FontManager.m new file mode 100644 index 0000000..12eac2d --- /dev/null +++ b/libs/FontLabel/FontManager.m @@ -0,0 +1,123 @@ +// +// FontManager.m +// FontLabel +// +// Created by Kevin Ballard on 5/5/09. +// Copyright © 2009 Zynga Game Networks +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "FontManager.h" +#import "ZFont.h" + +static FontManager *sharedFontManager = nil; + +@implementation FontManager ++ (FontManager *)sharedManager { + @synchronized(self) { + if (sharedFontManager == nil) { + sharedFontManager = [[self alloc] init]; + } + } + return sharedFontManager; +} + +- (id)init { + if ((self = [super init])) { + fonts = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + urls = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (BOOL)loadFont:(NSString *)filename { + NSString *fontPath = [[NSBundle mainBundle] pathForResource:filename ofType:@"ttf"]; + if (fontPath == nil) { + fontPath = [[NSBundle mainBundle] pathForResource:filename ofType:nil]; + } + if (fontPath == nil) return NO; + + NSURL *url = [NSURL fileURLWithPath:fontPath]; + if ([self loadFontURL:url]) { + [urls setObject:url forKey:filename]; + return YES; + } + return NO; +} + +- (BOOL)loadFontURL:(NSURL *)url { + CGDataProviderRef fontDataProvider = CGDataProviderCreateWithURL((CFURLRef)url); + if (fontDataProvider == NULL) return NO; + CGFontRef newFont = CGFontCreateWithDataProvider(fontDataProvider); + CGDataProviderRelease(fontDataProvider); + if (newFont == NULL) return NO; + + CFDictionarySetValue(fonts, url, newFont); + CGFontRelease(newFont); + return YES; +} + +- (CGFontRef)fontWithName:(NSString *)filename { + CGFontRef font = NULL; + NSURL *url = [urls objectForKey:filename]; + if (url == nil && [self loadFont:filename]) { + url = [urls objectForKey:filename]; + } + if (url != nil) { + font = (CGFontRef)CFDictionaryGetValue(fonts, url); + } + return font; +} + +- (ZFont *)zFontWithName:(NSString *)filename pointSize:(CGFloat)pointSize { + NSURL *url = [urls objectForKey:filename]; + if (url == nil && [self loadFont:filename]) { + url = [urls objectForKey:filename]; + } + if (url != nil) { + CGFontRef cgFont = (CGFontRef)CFDictionaryGetValue(fonts, url); + if (cgFont != NULL) { + return [ZFont fontWithCGFont:cgFont size:pointSize]; + } + } + return nil; +} + +- (ZFont *)zFontWithURL:(NSURL *)url pointSize:(CGFloat)pointSize { + CGFontRef cgFont = (CGFontRef)CFDictionaryGetValue(fonts, url); + if (cgFont == NULL && [self loadFontURL:url]) { + cgFont = (CGFontRef)CFDictionaryGetValue(fonts, url); + } + if (cgFont != NULL) { + return [ZFont fontWithCGFont:cgFont size:pointSize]; + } + return nil; +} + +- (CFArrayRef)copyAllFonts { + CFIndex count = CFDictionaryGetCount(fonts); + CGFontRef *values = (CGFontRef *)malloc(sizeof(CGFontRef) * count); + CFDictionaryGetKeysAndValues(fonts, NULL, (const void **)values); + CFArrayRef array = CFArrayCreate(NULL, (const void **)values, count, &kCFTypeArrayCallBacks); + free(values); + return array; +} + +- (void)dealloc { + CFRelease(fonts); + [urls release]; + [super dealloc]; +} +@end diff --git a/libs/FontLabel/ZAttributedString.h b/libs/FontLabel/ZAttributedString.h new file mode 100644 index 0000000..e194c81 --- /dev/null +++ b/libs/FontLabel/ZAttributedString.h @@ -0,0 +1,77 @@ +// +// ZAttributedString.h +// FontLabel +// +// Created by Kevin Ballard on 9/22/09. +// Copyright 2009 Zynga Game Networks. All rights reserved. +// + +#import + +#if NS_BLOCKS_AVAILABLE +#define Z_BLOCKS 1 +#else +// set this to 1 if you are using PLBlocks +#define Z_BLOCKS 0 +#endif + +#if Z_BLOCKS +enum { + ZAttributedStringEnumerationReverse = (1UL << 1), + ZAttributedStringEnumerationLongestEffectiveRangeNotRequired = (1UL << 20) +}; +typedef NSUInteger ZAttributedStringEnumerationOptions; +#endif + +@interface ZAttributedString : NSObject { + NSMutableString *_buffer; + NSMutableArray *_attributes; +} +@property (nonatomic, readonly) NSUInteger length; +@property (nonatomic, readonly) NSString *string; +- (id)initWithAttributedString:(ZAttributedString *)attr; +- (id)initWithString:(NSString *)str; +- (id)initWithString:(NSString *)str attributes:(NSDictionary *)attributes; +- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange; +- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange inRange:(NSRange)rangeLimit; +- (ZAttributedString *)attributedSubstringFromRange:(NSRange)aRange; +- (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange; +- (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange inRange:(NSRange)rangeLimit; +#if Z_BLOCKS +- (void)enumerateAttribute:(NSString *)attrName inRange:(NSRange)enumerationRange options:(ZAttributedStringEnumerationOptions)opts + usingBlock:(void (^)(id value, NSRange range, BOOL *stop))block; +- (void)enumerateAttributesInRange:(NSRange)enumerationRange options:(ZAttributedStringEnumerationOptions)opts + usingBlock:(void (^)(NSDictionary *attrs, NSRange range, BOOL *stop))block; +#endif +- (BOOL)isEqualToAttributedString:(ZAttributedString *)otherString; +@end + +@interface ZMutableAttributedString : ZAttributedString { +} +- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range; +- (void)addAttributes:(NSDictionary *)attributes range:(NSRange)range; +- (void)appendAttributedString:(ZAttributedString *)str; +- (void)deleteCharactersInRange:(NSRange)range; +- (void)insertAttributedString:(ZAttributedString *)str atIndex:(NSUInteger)idx; +- (void)removeAttribute:(NSString *)name range:(NSRange)range; +- (void)replaceCharactersInRange:(NSRange)range withAttributedString:(ZAttributedString *)str; +- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str; +- (void)setAttributedString:(ZAttributedString *)str; +- (void)setAttributes:(NSDictionary *)attributes range:(NSRange)range; +@end + +extern NSString * const ZFontAttributeName; +extern NSString * const ZForegroundColorAttributeName; +extern NSString * const ZBackgroundColorAttributeName; +extern NSString * const ZUnderlineStyleAttributeName; + +enum { + ZUnderlineStyleNone = 0x00, + ZUnderlineStyleSingle = 0x01 +}; +#define ZUnderlineStyleMask 0x00FF + +enum { + ZUnderlinePatternSolid = 0x0000 +}; +#define ZUnderlinePatternMask 0xFF00 diff --git a/libs/FontLabel/ZAttributedString.m b/libs/FontLabel/ZAttributedString.m new file mode 100644 index 0000000..79f0323 --- /dev/null +++ b/libs/FontLabel/ZAttributedString.m @@ -0,0 +1,596 @@ +// +// ZAttributedString.m +// FontLabel +// +// Created by Kevin Ballard on 9/22/09. +// Copyright 2009 Zynga Game Networks. All rights reserved. +// + +#import "ZAttributedString.h" +#import "ZAttributedStringPrivate.h" + +@interface ZAttributedString () +- (NSUInteger)indexOfEffectiveAttributeRunForIndex:(NSUInteger)index; +- (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange uniquingOnName:(NSString *)attributeName; +- (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange + inRange:(NSRange)rangeLimit uniquingOnName:(NSString *)attributeName; +@end + +@interface ZAttributedString () +@property (nonatomic, readonly) NSArray *attributes; +@end + +@implementation ZAttributedString +@synthesize string = _buffer; +@synthesize attributes = _attributes; + +- (id)initWithAttributedString:(ZAttributedString *)attr { + NSParameterAssert(attr != nil); + if ((self = [super init])) { + _buffer = [attr->_buffer mutableCopy]; + _attributes = [[NSMutableArray alloc] initWithArray:attr->_attributes copyItems:YES]; + } + return self; +} + +- (id)initWithString:(NSString *)str { + return [self initWithString:str attributes:nil]; +} + +- (id)initWithString:(NSString *)str attributes:(NSDictionary *)attributes { + if ((self = [super init])) { + _buffer = [str mutableCopy]; + _attributes = [[NSMutableArray alloc] initWithObjects:[ZAttributeRun attributeRunWithIndex:0 attributes:attributes], nil]; + } + return self; +} + +- (id)init { + return [self initWithString:@"" attributes:nil]; +} + +- (id)initWithCoder:(NSCoder *)decoder { + if ((self = [super init])) { + _buffer = [[decoder decodeObjectForKey:@"buffer"] mutableCopy]; + _attributes = [[decoder decodeObjectForKey:@"attributes"] mutableCopy]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_buffer forKey:@"buffer"]; + [aCoder encodeObject:_attributes forKey:@"attributes"]; +} + +- (id)copyWithZone:(NSZone *)zone { + return [self retain]; +} + +- (id)mutableCopyWithZone:(NSZone *)zone { + return [(ZMutableAttributedString *)[ZMutableAttributedString allocWithZone:zone] initWithAttributedString:self]; +} + +- (NSUInteger)length { + return [_buffer length]; +} + +- (NSString *)description { + NSMutableArray *components = [NSMutableArray arrayWithCapacity:[_attributes count]*2]; + NSRange range = NSMakeRange(0, 0); + for (NSUInteger i = 0; i <= [_attributes count]; i++) { + range.location = NSMaxRange(range); + ZAttributeRun *run; + if (i < [_attributes count]) { + run = [_attributes objectAtIndex:i]; + range.length = run.index - range.location; + } else { + run = nil; + range.length = [_buffer length] - range.location; + } + if (range.length > 0) { + [components addObject:[NSString stringWithFormat:@"\"%@\"", [_buffer substringWithRange:range]]]; + } + if (run != nil) { + NSMutableArray *attrDesc = [NSMutableArray arrayWithCapacity:[run.attributes count]]; + for (id key in run.attributes) { + [attrDesc addObject:[NSString stringWithFormat:@"%@: %@", key, [run.attributes objectForKey:key]]]; + } + [components addObject:[NSString stringWithFormat:@"{%@}", [attrDesc componentsJoinedByString:@", "]]]; + } + } + return [NSString stringWithFormat:@"%@", [components componentsJoinedByString:@" "]]; +} + +- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange { + NSParameterAssert(attributeName != nil); + return [[self attributesAtIndex:index effectiveRange:aRange uniquingOnName:attributeName] objectForKey:attributeName]; +} + +- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange inRange:(NSRange)rangeLimit { + NSParameterAssert(attributeName != nil); + return [[self attributesAtIndex:index longestEffectiveRange:aRange inRange:rangeLimit uniquingOnName:attributeName] objectForKey:attributeName]; +} + +- (ZAttributedString *)attributedSubstringFromRange:(NSRange)aRange { + if (NSMaxRange(aRange) > [_buffer length]) { + @throw [NSException exceptionWithName:NSRangeException reason:@"range was outisde of the attributed string" userInfo:nil]; + } + ZMutableAttributedString *newStr = [self mutableCopy]; + if (aRange.location > 0) { + [newStr deleteCharactersInRange:NSMakeRange(0, aRange.location)]; + } + if (NSMaxRange(aRange) < [_buffer length]) { + [newStr deleteCharactersInRange:NSMakeRange(aRange.length, [_buffer length] - NSMaxRange(aRange))]; + } + return [newStr autorelease]; +} + +- (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange { + return [NSDictionary dictionaryWithDictionary:[self attributesAtIndex:index effectiveRange:aRange uniquingOnName:nil]]; +} + +- (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange inRange:(NSRange)rangeLimit { + return [NSDictionary dictionaryWithDictionary:[self attributesAtIndex:index longestEffectiveRange:aRange inRange:rangeLimit uniquingOnName:nil]]; +} + +#if Z_BLOCKS +// Warning: this code has not been tested. The only guarantee is that it compiles. +- (void)enumerateAttribute:(NSString *)attrName inRange:(NSRange)enumerationRange options:(ZAttributedStringEnumerationOptions)opts + usingBlock:(void (^)(id, NSRange, BOOL*))block { + if (opts & ZAttributedStringEnumerationLongestEffectiveRangeNotRequired) { + [self enumerateAttributesInRange:enumerationRange options:opts usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { + id value = [attrs objectForKey:attrName]; + if (value != nil) { + block(value, range, stop); + } + }]; + } else { + __block id oldValue = nil; + __block NSRange effectiveRange = NSMakeRange(0, 0); + [self enumerateAttributesInRange:enumerationRange options:opts usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { + id value = [attrs objectForKey:attrName]; + if (oldValue == nil) { + oldValue = value; + effectiveRange = range; + } else if (value != nil && [oldValue isEqual:value]) { + // combine the attributes + effectiveRange = NSUnionRange(effectiveRange, range); + } else { + BOOL innerStop = NO; + block(oldValue, effectiveRange, &innerStop); + if (innerStop) { + *stop = YES; + oldValue = nil; + } else { + oldValue = value; + } + } + }]; + if (oldValue != nil) { + BOOL innerStop = NO; // necessary for the block, but unused + block(oldValue, effectiveRange, &innerStop); + } + } +} + +- (void)enumerateAttributesInRange:(NSRange)enumerationRange options:(ZAttributedStringEnumerationOptions)opts + usingBlock:(void (^)(NSDictionary*, NSRange, BOOL*))block { + // copy the attributes so we can mutate the string if necessary during enumeration + // also clip the array during copy to only the subarray of attributes that cover the requested range + NSArray *attrs; + if (NSEqualRanges(enumerationRange, NSMakeRange(0, 0))) { + attrs = [NSArray arrayWithArray:_attributes]; + } else { + // in this binary search, last is the first run after the range + NSUInteger first = 0, last = [_attributes count]; + while (last > first+1) { + NSUInteger pivot = (last + first) / 2; + ZAttributeRun *run = [_attributes objectAtIndex:pivot]; + if (run.index < enumerationRange.location) { + first = pivot; + } else if (run.index >= NSMaxRange(enumerationRange)) { + last = pivot; + } + } + attrs = [_attributes subarrayWithRange:NSMakeRange(first, last-first)]; + } + if (opts & ZAttributedStringEnumerationReverse) { + NSUInteger end = [_buffer length]; + for (ZAttributeRun *run in [attrs reverseObjectEnumerator]) { + BOOL stop = NO; + NSUInteger start = run.index; + // clip to enumerationRange + start = MAX(start, enumerationRange.location); + end = MIN(end, NSMaxRange(enumerationRange)); + block(run.attributes, NSMakeRange(start, end - start), &stop); + if (stop) break; + end = run.index; + } + } else { + NSUInteger start = 0; + ZAttributeRun *run = [attrs objectAtIndex:0]; + NSInteger offset = 0; + NSInteger oldLength = [_buffer length]; + for (NSUInteger i = 1;;i++) { + NSUInteger end; + if (i >= [attrs count]) { + end = oldLength; + } else { + end = [[attrs objectAtIndex:i] index]; + } + BOOL stop = NO; + NSUInteger clippedStart = MAX(start, enumerationRange.location); + NSUInteger clippedEnd = MIN(end, NSMaxRange(enumerationRange)); + block(run.attributes, NSMakeRange(clippedStart + offset, clippedEnd - start), &stop); + if (stop || i >= [attrs count]) break; + start = end; + NSUInteger newLength = [_buffer length]; + offset += (newLength - oldLength); + oldLength = newLength; + } + } +} +#endif + +- (BOOL)isEqualToAttributedString:(ZAttributedString *)otherString { + return ([_buffer isEqualToString:otherString->_buffer] && [_attributes isEqualToArray:otherString->_attributes]); +} + +- (BOOL)isEqual:(id)object { + return [object isKindOfClass:[ZAttributedString class]] && [self isEqualToAttributedString:(ZAttributedString *)object]; +} + +#pragma mark - + +- (NSUInteger)indexOfEffectiveAttributeRunForIndex:(NSUInteger)index { + NSUInteger first = 0, last = [_attributes count]; + while (last > first + 1) { + NSUInteger pivot = (last + first) / 2; + ZAttributeRun *run = [_attributes objectAtIndex:pivot]; + if (run.index > index) { + last = pivot; + } else if (run.index < index) { + first = pivot; + } else { + first = pivot; + break; + } + } + return first; +} + +- (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange uniquingOnName:(NSString *)attributeName { + if (index >= [_buffer length]) { + @throw [NSException exceptionWithName:NSRangeException reason:@"index beyond range of attributed string" userInfo:nil]; + } + NSUInteger runIndex = [self indexOfEffectiveAttributeRunForIndex:index]; + ZAttributeRun *run = [_attributes objectAtIndex:runIndex]; + if (aRange != NULL) { + aRange->location = run.index; + runIndex++; + if (runIndex < [_attributes count]) { + aRange->length = [[_attributes objectAtIndex:runIndex] index] - aRange->location; + } else { + aRange->length = [_buffer length] - aRange->location; + } + } + return run.attributes; +} +- (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange + inRange:(NSRange)rangeLimit uniquingOnName:(NSString *)attributeName { + if (index >= [_buffer length]) { + @throw [NSException exceptionWithName:NSRangeException reason:@"index beyond range of attributed string" userInfo:nil]; + } else if (NSMaxRange(rangeLimit) > [_buffer length]) { + @throw [NSException exceptionWithName:NSRangeException reason:@"rangeLimit beyond range of attributed string" userInfo:nil]; + } + NSUInteger runIndex = [self indexOfEffectiveAttributeRunForIndex:index]; + ZAttributeRun *run = [_attributes objectAtIndex:runIndex]; + if (aRange != NULL) { + if (attributeName != nil) { + id value = [run.attributes objectForKey:attributeName]; + NSUInteger endRunIndex = runIndex+1; + runIndex--; + // search backwards + while (1) { + if (run.index <= rangeLimit.location) { + break; + } + ZAttributeRun *prevRun = [_attributes objectAtIndex:runIndex]; + id prevValue = [prevRun.attributes objectForKey:attributeName]; + if (prevValue == value || (value != nil && [prevValue isEqual:value])) { + runIndex--; + run = prevRun; + } else { + break; + } + } + // search forwards + ZAttributeRun *endRun = nil; + while (endRunIndex < [_attributes count]) { + ZAttributeRun *nextRun = [_attributes objectAtIndex:endRunIndex]; + if (nextRun.index >= NSMaxRange(rangeLimit)) { + endRun = nextRun; + break; + } + id nextValue = [nextRun.attributes objectForKey:attributeName]; + if (nextValue == value || (value != nil && [nextValue isEqual:value])) { + endRunIndex++; + } else { + endRun = nextRun; + break; + } + } + aRange->location = MAX(run.index, rangeLimit.location); + aRange->length = MIN((endRun ? endRun.index : [_buffer length]), NSMaxRange(rangeLimit)) - aRange->location; + } else { + // with no attribute name, we don't need to do any real searching, + // as we already guarantee each run has unique attributes. + // just make sure to clip the range to the rangeLimit + aRange->location = MAX(run.index, rangeLimit.location); + ZAttributeRun *endRun = (runIndex+1 < [_attributes count] ? [_attributes objectAtIndex:runIndex+1] : nil); + aRange->length = MIN((endRun ? endRun.index : [_buffer length]), NSMaxRange(rangeLimit)) - aRange->location; + } + } + return run.attributes; +} + +- (void)dealloc { + [_buffer release]; + [_attributes release]; + [super dealloc]; +} +@end + +@interface ZMutableAttributedString () +- (void)cleanupAttributesInRange:(NSRange)range; +- (NSRange)rangeOfAttributeRunsForRange:(NSRange)range; +- (void)offsetRunsInRange:(NSRange )range byOffset:(NSInteger)offset; +@end + +@implementation ZMutableAttributedString +- (id)copyWithZone:(NSZone *)zone { + return [(ZAttributedString *)[ZAttributedString allocWithZone:zone] initWithAttributedString:self]; +} + +- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range { + range = [self rangeOfAttributeRunsForRange:range]; + for (ZAttributeRun *run in [_attributes subarrayWithRange:range]) { + [run.attributes setObject:value forKey:name]; + } + [self cleanupAttributesInRange:range]; +} + +- (void)addAttributes:(NSDictionary *)attributes range:(NSRange)range { + range = [self rangeOfAttributeRunsForRange:range]; + for (ZAttributeRun *run in [_attributes subarrayWithRange:range]) { + [run.attributes addEntriesFromDictionary:attributes]; + } + [self cleanupAttributesInRange:range]; +} + +- (void)appendAttributedString:(ZAttributedString *)str { + [self insertAttributedString:str atIndex:[_buffer length]]; +} + +- (void)deleteCharactersInRange:(NSRange)range { + NSRange runRange = [self rangeOfAttributeRunsForRange:range]; + [_buffer replaceCharactersInRange:range withString:@""]; + [_attributes removeObjectsInRange:runRange]; + for (NSUInteger i = runRange.location; i < [_attributes count]; i++) { + ZAttributeRun *run = [_attributes objectAtIndex:i]; + ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:(run.index - range.length) attributes:run.attributes]; + [_attributes replaceObjectAtIndex:i withObject:newRun]; + [newRun release]; + } + [self cleanupAttributesInRange:NSMakeRange(runRange.location, 0)]; +} + +- (void)insertAttributedString:(ZAttributedString *)str atIndex:(NSUInteger)idx { + [self replaceCharactersInRange:NSMakeRange(idx, 0) withAttributedString:str]; +} + +- (void)removeAttribute:(NSString *)name range:(NSRange)range { + range = [self rangeOfAttributeRunsForRange:range]; + for (ZAttributeRun *run in [_attributes subarrayWithRange:range]) { + [run.attributes removeObjectForKey:name]; + } + [self cleanupAttributesInRange:range]; +} + +- (void)replaceCharactersInRange:(NSRange)range withAttributedString:(ZAttributedString *)str { + NSRange replaceRange = [self rangeOfAttributeRunsForRange:range]; + NSInteger offset = [str->_buffer length] - range.length; + [_buffer replaceCharactersInRange:range withString:str->_buffer]; + [_attributes replaceObjectsInRange:replaceRange withObjectsFromArray:str->_attributes]; + NSRange newRange = NSMakeRange(replaceRange.location, [str->_attributes count]); + [self offsetRunsInRange:newRange byOffset:range.location]; + [self offsetRunsInRange:NSMakeRange(NSMaxRange(newRange), [_attributes count] - NSMaxRange(newRange)) byOffset:offset]; + [self cleanupAttributesInRange:NSMakeRange(newRange.location, 0)]; + [self cleanupAttributesInRange:NSMakeRange(NSMaxRange(newRange), 0)]; +} + +- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str { + [self replaceCharactersInRange:range withAttributedString:[[[ZAttributedString alloc] initWithString:str] autorelease]]; +} + +- (void)setAttributedString:(ZAttributedString *)str { + [_buffer release], _buffer = [str->_buffer mutableCopy]; + [_attributes release], _attributes = [str->_attributes mutableCopy]; +} + +- (void)setAttributes:(NSDictionary *)attributes range:(NSRange)range { + range = [self rangeOfAttributeRunsForRange:range]; + for (ZAttributeRun *run in [_attributes subarrayWithRange:range]) { + [run.attributes setDictionary:attributes]; + } + [self cleanupAttributesInRange:range]; +} + +#pragma mark - + +// splits the existing runs to provide one or more new runs for the given range +- (NSRange)rangeOfAttributeRunsForRange:(NSRange)range { + NSParameterAssert(NSMaxRange(range) <= [_buffer length]); + + // find (or create) the first run + NSUInteger first = 0; + ZAttributeRun *lastRun = nil; + for (;;first++) { + if (first >= [_attributes count]) { + // we didn't find a run + first = [_attributes count]; + ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:range.location attributes:lastRun.attributes]; + [_attributes addObject:newRun]; + [newRun release]; + break; + } + ZAttributeRun *run = [_attributes objectAtIndex:first]; + if (run.index == range.location) { + break; + } else if (run.index > range.location) { + ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:range.location attributes:lastRun.attributes]; + [_attributes insertObject:newRun atIndex:first]; + [newRun release]; + break; + } + lastRun = run; + } + + if (((ZAttributeRun *)[_attributes lastObject]).index < NSMaxRange(range)) { + NSRange subrange = NSMakeRange(first, [_attributes count] - first); + if (NSMaxRange(range) < [_buffer length]) { + ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:NSMaxRange(range) attributes:[[_attributes lastObject] attributes]]; + [_attributes addObject:newRun]; + [newRun release]; + } + return subrange; + } else { + // find the last run within and the first run after the range + NSUInteger lastIn = first, firstAfter = [_attributes count]-1; + while (firstAfter > lastIn + 1) { + NSUInteger idx = (firstAfter + lastIn) / 2; + ZAttributeRun *run = [_attributes objectAtIndex:idx]; + if (run.index < range.location) { + lastIn = idx; + } else if (run.index > range.location) { + firstAfter = idx; + } else { + // this is definitively the first run after the range + firstAfter = idx; + break; + } + } + if ([[_attributes objectAtIndex:firstAfter] index] > NSMaxRange(range)) { + // the first after is too far after, insert another run! + ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:NSMaxRange(range) + attributes:[[_attributes objectAtIndex:firstAfter-1] attributes]]; + [_attributes insertObject:newRun atIndex:firstAfter]; + [newRun release]; + } + return NSMakeRange(lastIn, firstAfter - lastIn); + } +} + +- (void)cleanupAttributesInRange:(NSRange)range { + // expand the range to include one surrounding attribute on each side + if (range.location > 0) { + range.location -= 1; + range.length += 1; + } + if (NSMaxRange(range) < [_attributes count]) { + range.length += 1; + } else { + // make sure the range is capped to the attributes count + range.length = [_attributes count] - range.location; + } + if (range.length == 0) return; + ZAttributeRun *lastRun = [_attributes objectAtIndex:range.location]; + for (NSUInteger i = range.location+1; i < NSMaxRange(range);) { + ZAttributeRun *run = [_attributes objectAtIndex:i]; + if ([lastRun.attributes isEqualToDictionary:run.attributes]) { + [_attributes removeObjectAtIndex:i]; + range.length -= 1; + } else { + lastRun = run; + i++; + } + } +} + +- (void)offsetRunsInRange:(NSRange)range byOffset:(NSInteger)offset { + for (NSUInteger i = range.location; i < NSMaxRange(range); i++) { + ZAttributeRun *run = [_attributes objectAtIndex:i]; + ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:run.index + offset attributes:run.attributes]; + [_attributes replaceObjectAtIndex:i withObject:newRun]; + [newRun release]; + } +} +@end + +@implementation ZAttributeRun +@synthesize index = _index; +@synthesize attributes = _attributes; + ++ (id)attributeRunWithIndex:(NSUInteger)idx attributes:(NSDictionary *)attrs { + return [[[self alloc] initWithIndex:idx attributes:attrs] autorelease]; +} + +- (id)initWithIndex:(NSUInteger)idx attributes:(NSDictionary *)attrs { + NSParameterAssert(idx >= 0); + if ((self = [super init])) { + _index = idx; + if (attrs == nil) { + _attributes = [[NSMutableDictionary alloc] init]; + } else { + _attributes = [attrs mutableCopy]; + } + } + return self; +} + +- (id)initWithCoder:(NSCoder *)decoder { + if ((self = [super init])) { + _index = [[decoder decodeObjectForKey:@"index"] unsignedIntegerValue]; + _attributes = [[decoder decodeObjectForKey:@"attributes"] mutableCopy]; + } + return self; +} + +- (id)init { + return [self initWithIndex:0 attributes:[NSDictionary dictionary]]; +} + +- (id)copyWithZone:(NSZone *)zone { + return [[ZAttributeRun allocWithZone:zone] initWithIndex:_index attributes:_attributes]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:[NSNumber numberWithUnsignedInteger:_index] forKey:@"index"]; + [aCoder encodeObject:_attributes forKey:@"attributes"]; +} + +- (NSString *)description { + NSMutableArray *components = [NSMutableArray arrayWithCapacity:[_attributes count]]; + for (id key in _attributes) { + [components addObject:[NSString stringWithFormat:@"%@=%@", key, [_attributes objectForKey:key]]]; + } + return [NSString stringWithFormat:@"<%@: %p index=%lu attributes={%@}>", + NSStringFromClass([self class]), self, (unsigned long)_index, [components componentsJoinedByString:@" "]]; +} + +- (BOOL)isEqual:(id)object { + if (![object isKindOfClass:[ZAttributeRun class]]) return NO; + ZAttributeRun *other = (ZAttributeRun *)object; + return _index == other->_index && [_attributes isEqualToDictionary:other->_attributes]; +} + +- (void)dealloc { + [_attributes release]; + [super dealloc]; +} +@end + +NSString * const ZFontAttributeName = @"ZFontAttributeName"; +NSString * const ZForegroundColorAttributeName = @"ZForegroundColorAttributeName"; +NSString * const ZBackgroundColorAttributeName = @"ZBackgroundColorAttributeName"; +NSString * const ZUnderlineStyleAttributeName = @"ZUnderlineStyleAttributeName"; diff --git a/libs/FontLabel/ZAttributedStringPrivate.h b/libs/FontLabel/ZAttributedStringPrivate.h new file mode 100644 index 0000000..1021d7b --- /dev/null +++ b/libs/FontLabel/ZAttributedStringPrivate.h @@ -0,0 +1,24 @@ +// +// ZAttributedStringPrivate.h +// FontLabel +// +// Created by Kevin Ballard on 9/23/09. +// Copyright 2009 Zynga Game Networks. All rights reserved. +// + +#import +#import "ZAttributedString.h" + +@interface ZAttributeRun : NSObject { + NSUInteger _index; + NSMutableDictionary *_attributes; +} +@property (nonatomic, readonly) NSUInteger index; +@property (nonatomic, readonly) NSMutableDictionary *attributes; ++ (id)attributeRunWithIndex:(NSUInteger)idx attributes:(NSDictionary *)attrs; +- (id)initWithIndex:(NSUInteger)idx attributes:(NSDictionary *)attrs; +@end + +@interface ZAttributedString (ZAttributedStringPrivate) +@property (nonatomic, readonly) NSArray *attributes; +@end diff --git a/libs/FontLabel/ZFont.h b/libs/FontLabel/ZFont.h new file mode 100644 index 0000000..05ae823 --- /dev/null +++ b/libs/FontLabel/ZFont.h @@ -0,0 +1,47 @@ +// +// ZFont.h +// FontLabel +// +// Created by Kevin Ballard on 7/2/09. +// Copyright © 2009 Zynga Game Networks +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import + +@interface ZFont : NSObject { + CGFontRef _cgFont; + CGFloat _pointSize; + CGFloat _ratio; + NSString *_familyName; + NSString *_fontName; + NSString *_postScriptName; +} +@property (nonatomic, readonly) CGFontRef cgFont; +@property (nonatomic, readonly) CGFloat pointSize; +@property (nonatomic, readonly) CGFloat ascender; +@property (nonatomic, readonly) CGFloat descender; +@property (nonatomic, readonly) CGFloat leading; +@property (nonatomic, readonly) CGFloat xHeight; +@property (nonatomic, readonly) CGFloat capHeight; +@property (nonatomic, readonly) NSString *familyName; +@property (nonatomic, readonly) NSString *fontName; +@property (nonatomic, readonly) NSString *postScriptName; ++ (ZFont *)fontWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize; ++ (ZFont *)fontWithUIFont:(UIFont *)uiFont; +- (id)initWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize; +- (ZFont *)fontWithSize:(CGFloat)fontSize; +@end diff --git a/libs/FontLabel/ZFont.m b/libs/FontLabel/ZFont.m new file mode 100644 index 0000000..793b13a --- /dev/null +++ b/libs/FontLabel/ZFont.m @@ -0,0 +1,170 @@ +// +// ZFont.m +// FontLabel +// +// Created by Kevin Ballard on 7/2/09. +// Copyright © 2009 Zynga Game Networks +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "ZFont.h" + +@interface ZFont () +@property (nonatomic, readonly) CGFloat ratio; +- (NSString *)copyNameTableEntryForID:(UInt16)nameID; +@end + +@implementation ZFont +@synthesize cgFont=_cgFont, pointSize=_pointSize, ratio=_ratio; + ++ (ZFont *)fontWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize { + return [[[self alloc] initWithCGFont:cgFont size:fontSize] autorelease]; +} + ++ (ZFont *)fontWithUIFont:(UIFont *)uiFont { + NSParameterAssert(uiFont != nil); + CGFontRef cgFont = CGFontCreateWithFontName((CFStringRef)uiFont.fontName); + ZFont *zFont = [[self alloc] initWithCGFont:cgFont size:uiFont.pointSize]; + CGFontRelease(cgFont); + return [zFont autorelease]; +} + +- (id)initWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize { + if ((self = [super init])) { + _cgFont = CGFontRetain(cgFont); + _pointSize = fontSize; + _ratio = fontSize/CGFontGetUnitsPerEm(cgFont); + } + return self; +} + +- (id)init { + NSAssert(NO, @"-init is not valid for ZFont"); + return nil; +} + +- (CGFloat)ascender { + return ceilf(self.ratio * CGFontGetAscent(self.cgFont)); +} + +- (CGFloat)descender { + return floorf(self.ratio * CGFontGetDescent(self.cgFont)); +} + +- (CGFloat)leading { + return (self.ascender - self.descender); +} + +- (CGFloat)capHeight { + return ceilf(self.ratio * CGFontGetCapHeight(self.cgFont)); +} + +- (CGFloat)xHeight { + return ceilf(self.ratio * CGFontGetXHeight(self.cgFont)); +} + +- (NSString *)familyName { + if (_familyName == nil) { + _familyName = [self copyNameTableEntryForID:1]; + } + return _familyName; +} + +- (NSString *)fontName { + if (_fontName == nil) { + _fontName = [self copyNameTableEntryForID:4]; + } + return _fontName; +} + +- (NSString *)postScriptName { + if (_postScriptName == nil) { + _postScriptName = [self copyNameTableEntryForID:6]; + } + return _postScriptName; +} + +- (ZFont *)fontWithSize:(CGFloat)fontSize { + if (fontSize == self.pointSize) return self; + NSParameterAssert(fontSize > 0.0); + return [[[ZFont alloc] initWithCGFont:self.cgFont size:fontSize] autorelease]; +} + +- (BOOL)isEqual:(id)object { + if (![object isKindOfClass:[ZFont class]]) return NO; + ZFont *font = (ZFont *)object; + return (font.cgFont == self.cgFont && font.pointSize == self.pointSize); +} + +- (NSString *)copyNameTableEntryForID:(UInt16)aNameID { + CFDataRef nameTable = CGFontCopyTableForTag(self.cgFont, 'name'); + NSAssert1(nameTable != NULL, @"CGFontCopyTableForTag returned NULL for 'name' tag in font %@", + [(id)CFCopyDescription(self.cgFont) autorelease]); + const UInt8 * const bytes = CFDataGetBytePtr(nameTable); + NSAssert1(OSReadBigInt16(bytes, 0) == 0, @"name table for font %@ has bad version number", + [(id)CFCopyDescription(self.cgFont) autorelease]); + const UInt16 count = OSReadBigInt16(bytes, 2); + const UInt16 stringOffset = OSReadBigInt16(bytes, 4); + const UInt8 * const nameRecords = &bytes[6]; + UInt16 nameLength = 0; + UInt16 nameOffset = 0; + NSStringEncoding encoding = 0; + for (UInt16 idx = 0; idx < count; idx++) { + const uintptr_t recordOffset = 12 * idx; + const UInt16 nameID = OSReadBigInt16(nameRecords, recordOffset + 6); + if (nameID != aNameID) continue; + const UInt16 platformID = OSReadBigInt16(nameRecords, recordOffset + 0); + const UInt16 platformSpecificID = OSReadBigInt16(nameRecords, recordOffset + 2); + encoding = 0; + // for now, we only support a subset of encodings + switch (platformID) { + case 0: // Unicode + encoding = NSUTF16StringEncoding; + break; + case 1: // Macintosh + switch (platformSpecificID) { + case 0: + encoding = NSMacOSRomanStringEncoding; + break; + } + case 3: // Microsoft + switch (platformSpecificID) { + case 1: + encoding = NSUTF16StringEncoding; + break; + } + } + if (encoding == 0) continue; + nameLength = OSReadBigInt16(nameRecords, recordOffset + 8); + nameOffset = OSReadBigInt16(nameRecords, recordOffset + 10); + break; + } + NSString *result = nil; + if (nameOffset > 0) { + const UInt8 *nameBytes = &bytes[stringOffset + nameOffset]; + result = [[NSString alloc] initWithBytes:nameBytes length:nameLength encoding:encoding]; + } + CFRelease(nameTable); + return result; +} + +- (void)dealloc { + CGFontRelease(_cgFont); + [_familyName release]; + [_fontName release]; + [_postScriptName release]; + [super dealloc]; +} +@end diff --git a/libs/README b/libs/README new file mode 100644 index 0000000..0d4d8fc --- /dev/null +++ b/libs/README @@ -0,0 +1 @@ +This directory contains the libraries used in cocos2d diff --git a/libs/TouchJSON/CDataScanner.h b/libs/TouchJSON/CDataScanner.h new file mode 100644 index 0000000..41f68e8 --- /dev/null +++ b/libs/TouchJSON/CDataScanner.h @@ -0,0 +1,71 @@ +// +// CDataScanner.h +// TouchCode +// +// Created by Jonathan Wight on 04/16/08. +// Copyright 2008 toxicsoftware.com. All rights reserved. +// +// 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 + +// NSScanner + +@interface CDataScanner : NSObject { + NSData *data; + + u_int8_t *start; + u_int8_t *end; + u_int8_t *current; + NSUInteger length; +} + +@property (readwrite, nonatomic, retain) NSData *data; +@property (readwrite, nonatomic, assign) NSUInteger scanLocation; +@property (readonly, nonatomic, assign) NSUInteger bytesRemaining; +@property (readonly, nonatomic, assign) BOOL isAtEnd; + +- (id)initWithData:(NSData *)inData; + +- (unichar)currentCharacter; +- (unichar)scanCharacter; +- (BOOL)scanCharacter:(unichar)inCharacter; + +- (BOOL)scanUTF8String:(const char *)inString intoString:(NSString **)outValue; +- (BOOL)scanString:(NSString *)inString intoString:(NSString **)outValue; +- (BOOL)scanCharactersFromSet:(NSCharacterSet *)inSet intoString:(NSString **)outValue; // inSet must only contain 7-bit ASCII characters + +- (BOOL)scanUpToString:(NSString *)string intoString:(NSString **)outValue; +- (BOOL)scanUpToCharactersFromSet:(NSCharacterSet *)set intoString:(NSString **)outValue; // inSet must only contain 7-bit ASCII characters + +- (BOOL)scanNumber:(NSNumber **)outValue; +- (BOOL)scanDecimalNumber:(NSDecimalNumber **)outValue; + +- (BOOL)scanDataOfLength:(NSUInteger)inLength intoData:(NSData **)outData; + +- (void)skipWhitespace; + +- (NSString *)remainingString; +- (NSData *)remainingData; + +@end diff --git a/libs/TouchJSON/CDataScanner.m b/libs/TouchJSON/CDataScanner.m new file mode 100644 index 0000000..b3cee6f --- /dev/null +++ b/libs/TouchJSON/CDataScanner.m @@ -0,0 +1,340 @@ +// +// CDataScanner.m +// TouchCode +// +// Created by Jonathan Wight on 04/16/08. +// Copyright 2008 toxicsoftware.com. All rights reserved. +// +// 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 "CDataScanner.h" + +#import "CDataScanner_Extensions.h" + +@interface CDataScanner () +@end + +#pragma mark - + +inline static unichar CharacterAtPointer(void *start, void *end) + { + #pragma unused(end) + + const u_int8_t theByte = *(u_int8_t *)start; + if (theByte & 0x80) + { + // TODO -- UNICODE!!!! (well in theory nothing todo here) + } + const unichar theCharacter = theByte; + return(theCharacter); + } + + static NSCharacterSet *sDoubleCharacters = NULL; + + @implementation CDataScanner + +- (id)init + { + if ((self = [super init]) != NULL) + { + } + return(self); + } + +- (id)initWithData:(NSData *)inData; + { + if ((self = [self init]) != NULL) + { + [self setData:inData]; + } + return(self); + } + + + (void)initialize + { + if (sDoubleCharacters == NULL) + { + sDoubleCharacters = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789eE-+."] retain]; + } + } + +- (void)dealloc + { + [data release]; + data = NULL; + // + [super dealloc]; + } + +- (NSUInteger)scanLocation + { + return(current - start); + } + +- (NSUInteger)bytesRemaining + { + return(end - current); + } + +- (NSData *)data + { + return(data); + } + +- (void)setData:(NSData *)inData + { + if (data != inData) + { + [data release]; + data = [inData retain]; + } + + if (data) + { + start = (u_int8_t *)data.bytes; + end = start + data.length; + current = start; + length = data.length; + } + else + { + start = NULL; + end = NULL; + current = NULL; + length = 0; + } + } + +- (void)setScanLocation:(NSUInteger)inScanLocation + { + current = start + inScanLocation; + } + +- (BOOL)isAtEnd + { + return(self.scanLocation >= length); + } + +- (unichar)currentCharacter + { + return(CharacterAtPointer(current, end)); + } + +#pragma mark - + +- (unichar)scanCharacter + { + const unichar theCharacter = CharacterAtPointer(current++, end); + return(theCharacter); + } + +- (BOOL)scanCharacter:(unichar)inCharacter + { + unichar theCharacter = CharacterAtPointer(current, end); + if (theCharacter == inCharacter) + { + ++current; + return(YES); + } + else + return(NO); + } + +- (BOOL)scanUTF8String:(const char *)inString intoString:(NSString **)outValue + { + const size_t theLength = strlen(inString); + if ((size_t)(end - current) < theLength) + return(NO); + if (strncmp((char *)current, inString, theLength) == 0) + { + current += theLength; + if (outValue) + *outValue = [NSString stringWithUTF8String:inString]; + return(YES); + } + return(NO); + } + +- (BOOL)scanString:(NSString *)inString intoString:(NSString **)outValue + { + if ((size_t)(end - current) < inString.length) + return(NO); + if (strncmp((char *)current, [inString UTF8String], inString.length) == 0) + { + current += inString.length; + if (outValue) + *outValue = inString; + return(YES); + } + return(NO); + } + +- (BOOL)scanCharactersFromSet:(NSCharacterSet *)inSet intoString:(NSString **)outValue + { + u_int8_t *P; + for (P = current; P < end && [inSet characterIsMember:*P] == YES; ++P) + ; + + if (P == current) + { + return(NO); + } + + if (outValue) + { + *outValue = [[[NSString alloc] initWithBytes:current length:P - current encoding:NSUTF8StringEncoding] autorelease]; + } + + current = P; + + return(YES); + } + +- (BOOL)scanUpToString:(NSString *)inString intoString:(NSString **)outValue + { + const char *theToken = [inString UTF8String]; + const char *theResult = strnstr((char *)current, theToken, end - current); + if (theResult == NULL) + { + return(NO); + } + + if (outValue) + { + *outValue = [[[NSString alloc] initWithBytes:current length:theResult - (char *)current encoding:NSUTF8StringEncoding] autorelease]; + } + + current = (u_int8_t *)theResult; + + return(YES); + } + +- (BOOL)scanUpToCharactersFromSet:(NSCharacterSet *)inSet intoString:(NSString **)outValue + { + u_int8_t *P; + for (P = current; P < end && [inSet characterIsMember:*P] == NO; ++P) + ; + + if (P == current) + { + return(NO); + } + + if (outValue) + { + *outValue = [[[NSString alloc] initWithBytes:current length:P - current encoding:NSUTF8StringEncoding] autorelease]; + } + + current = P; + + return(YES); + } + +- (BOOL)scanNumber:(NSNumber **)outValue + { + NSString *theString = NULL; + if ([self scanCharactersFromSet:sDoubleCharacters intoString:&theString]) + { + if ([theString rangeOfString:@"."].location != NSNotFound) + { + if (outValue) + { + *outValue = [NSDecimalNumber decimalNumberWithString:theString]; + } + return(YES); + } + else if ([theString rangeOfString:@"-"].location != NSNotFound) + { + if (outValue != NULL) + { + *outValue = [NSNumber numberWithLongLong:[theString longLongValue]]; + } + return(YES); + } + else + { + if (outValue != NULL) + { + *outValue = [NSNumber numberWithUnsignedLongLong:strtoull([theString UTF8String], NULL, 0)]; + } + return(YES); + } + + } + return(NO); + } + +- (BOOL)scanDecimalNumber:(NSDecimalNumber **)outValue; + { + NSString *theString = NULL; + if ([self scanCharactersFromSet:sDoubleCharacters intoString:&theString]) + { + if (outValue) + { + *outValue = [NSDecimalNumber decimalNumberWithString:theString]; + } + return(YES); + } + return(NO); + } + +- (BOOL)scanDataOfLength:(NSUInteger)inLength intoData:(NSData **)outData; + { + if (self.bytesRemaining < inLength) + { + return(NO); + } + + if (outData) + { + *outData = [NSData dataWithBytes:current length:inLength]; + } + + current += inLength; + return(YES); + } + + +- (void)skipWhitespace + { + u_int8_t *P; + for (P = current; P < end && (isspace(*P)); ++P) + ; + + current = P; + } + +- (NSString *)remainingString + { + NSData *theRemainingData = [NSData dataWithBytes:current length:end - current]; + NSString *theString = [[[NSString alloc] initWithData:theRemainingData encoding:NSUTF8StringEncoding] autorelease]; + return(theString); + } + +- (NSData *)remainingData; + { + NSData *theRemainingData = [NSData dataWithBytes:current length:end - current]; + return(theRemainingData); + } + + @end diff --git a/libs/TouchJSON/Extensions/CDataScanner_Extensions.h b/libs/TouchJSON/Extensions/CDataScanner_Extensions.h new file mode 100644 index 0000000..cde1dbb --- /dev/null +++ b/libs/TouchJSON/Extensions/CDataScanner_Extensions.h @@ -0,0 +1,40 @@ +// +// CDataScanner_Extensions.h +// TouchCode +// +// Created by Jonathan Wight on 12/08/2005. +// Copyright 2005 toxicsoftware.com. All rights reserved. +// +// 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 "CDataScanner.h" + +@interface CDataScanner (CDataScanner_Extensions) + +- (BOOL)scanCStyleComment:(NSString **)outComment; +- (BOOL)scanCPlusPlusStyleComment:(NSString **)outComment; + +- (NSUInteger)lineOfScanLocation; +- (NSDictionary *)userInfoForScanLocation; + +@end diff --git a/libs/TouchJSON/Extensions/CDataScanner_Extensions.m b/libs/TouchJSON/Extensions/CDataScanner_Extensions.m new file mode 100644 index 0000000..90dbbda --- /dev/null +++ b/libs/TouchJSON/Extensions/CDataScanner_Extensions.m @@ -0,0 +1,135 @@ +// +// CDataScanner_Extensions.m +// TouchCode +// +// Created by Jonathan Wight on 12/08/2005. +// Copyright 2005 toxicsoftware.com. All rights reserved. +// +// 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 "CDataScanner_Extensions.h" + +#define LF 0x000a // Line Feed +#define FF 0x000c // Form Feed +#define CR 0x000d // Carriage Return +#define NEL 0x0085 // Next Line +#define LS 0x2028 // Line Separator +#define PS 0x2029 // Paragraph Separator + +@implementation CDataScanner (CDataScanner_Extensions) + +- (BOOL)scanCStyleComment:(NSString **)outComment +{ +if ([self scanString:@"/*" intoString:NULL] == YES) + { + NSString *theComment = NULL; + if ([self scanUpToString:@"*/" intoString:&theComment] == NO) + [NSException raise:NSGenericException format:@"Started to scan a C style comment but it wasn't terminated."]; + + if ([theComment rangeOfString:@"/*"].location != NSNotFound) + [NSException raise:NSGenericException format:@"C style comments should not be nested."]; + + if ([self scanString:@"*/" intoString:NULL] == NO) + [NSException raise:NSGenericException format:@"C style comment did not end correctly."]; + + if (outComment != NULL) + *outComment = theComment; + + return(YES); + } +else + { + return(NO); + } +} + +- (BOOL)scanCPlusPlusStyleComment:(NSString **)outComment + { + if ([self scanString:@"//" intoString:NULL] == YES) + { + unichar theCharacters[] = { LF, FF, CR, NEL, LS, PS, }; + NSCharacterSet *theLineBreaksCharacterSet = [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithCharacters:theCharacters length:sizeof(theCharacters) / sizeof(*theCharacters)]]; + + NSString *theComment = NULL; + [self scanUpToCharactersFromSet:theLineBreaksCharacterSet intoString:&theComment]; + [self scanCharactersFromSet:theLineBreaksCharacterSet intoString:NULL]; + + if (outComment != NULL) + *outComment = theComment; + + return(YES); + } + else + { + return(NO); + } + } + +- (NSUInteger)lineOfScanLocation + { + NSUInteger theLine = 0; + for (const u_int8_t *C = start; C < current; ++C) + { + // TODO: JIW What about MS-DOS line endings you bastard! (Also other unicode line endings) + if (*C == '\n' || *C == '\r') + { + ++theLine; + } + } + return(theLine); + } + +- (NSDictionary *)userInfoForScanLocation + { + NSUInteger theLine = 0; + const u_int8_t *theLineStart = start; + for (const u_int8_t *C = start; C < current; ++C) + { + if (*C == '\n' || *C == '\r') + { + theLineStart = C - 1; + ++theLine; + } + } + + NSUInteger theCharacter = current - theLineStart; + + NSRange theStartRange = NSIntersectionRange((NSRange){ .location = MAX((NSInteger)self.scanLocation - 20, 0), .length = 20 + (NSInteger)self.scanLocation - 20 }, (NSRange){ .location = 0, .length = self.data.length }); + NSRange theEndRange = NSIntersectionRange((NSRange){ .location = self.scanLocation, .length = 20 }, (NSRange){ .location = 0, .length = self.data.length }); + + + NSString *theSnippet = [NSString stringWithFormat:@"%@!HERE>!%@", + [[[NSString alloc] initWithData:[self.data subdataWithRange:theStartRange] encoding:NSUTF8StringEncoding] autorelease], + [[[NSString alloc] initWithData:[self.data subdataWithRange:theEndRange] encoding:NSUTF8StringEncoding] autorelease] + ]; + + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithUnsignedInteger:theLine], @"line", + [NSNumber numberWithUnsignedInteger:theCharacter], @"character", + [NSNumber numberWithUnsignedInteger:self.scanLocation], @"location", + theSnippet, @"snippet", + NULL]; + return(theUserInfo); + } + +@end diff --git a/libs/TouchJSON/Extensions/NSDictionary_JSONExtensions.h b/libs/TouchJSON/Extensions/NSDictionary_JSONExtensions.h new file mode 100644 index 0000000..6e611d0 --- /dev/null +++ b/libs/TouchJSON/Extensions/NSDictionary_JSONExtensions.h @@ -0,0 +1,37 @@ +// +// NSDictionary_JSONExtensions.h +// TouchCode +// +// Created by Jonathan Wight on 04/17/08. +// Copyright 2008 toxicsoftware.com. All rights reserved. +// +// 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 + +@interface NSDictionary (NSDictionary_JSONExtensions) + ++ (id)dictionaryWithJSONData:(NSData *)inData error:(NSError **)outError; ++ (id)dictionaryWithJSONString:(NSString *)inJSON error:(NSError **)outError; + +@end diff --git a/libs/TouchJSON/Extensions/NSDictionary_JSONExtensions.m b/libs/TouchJSON/Extensions/NSDictionary_JSONExtensions.m new file mode 100644 index 0000000..c0bb43c --- /dev/null +++ b/libs/TouchJSON/Extensions/NSDictionary_JSONExtensions.m @@ -0,0 +1,47 @@ +// +// NSDictionary_JSONExtensions.m +// TouchCode +// +// Created by Jonathan Wight on 04/17/08. +// Copyright 2008 toxicsoftware.com. All rights reserved. +// +// 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 "NSDictionary_JSONExtensions.h" + +#import "CJSONDeserializer.h" + +@implementation NSDictionary (NSDictionary_JSONExtensions) + ++ (id)dictionaryWithJSONData:(NSData *)inData error:(NSError **)outError + { + return([[CJSONDeserializer deserializer] deserialize:inData error:outError]); + } + ++ (id)dictionaryWithJSONString:(NSString *)inJSON error:(NSError **)outError; + { + NSData *theData = [inJSON dataUsingEncoding:NSUTF8StringEncoding]; + return([self dictionaryWithJSONData:theData error:outError]); + } + +@end diff --git a/libs/TouchJSON/JSON/CJSONDeserializer.h b/libs/TouchJSON/JSON/CJSONDeserializer.h new file mode 100644 index 0000000..0c3ed02 --- /dev/null +++ b/libs/TouchJSON/JSON/CJSONDeserializer.h @@ -0,0 +1,63 @@ +// +// CJSONDeserializer.h +// TouchCode +// +// Created by Jonathan Wight on 12/15/2005. +// Copyright 2005 toxicsoftware.com. All rights reserved. +// +// 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 + +#import "CJSONScanner.h" + +extern NSString *const kJSONDeserializerErrorDomain /* = @"CJSONDeserializerErrorDomain" */; + +enum { + kJSONDeserializationOptions_MutableContainers = kJSONScannerOptions_MutableContainers, + kJSONDeserializationOptions_MutableLeaves = kJSONScannerOptions_MutableLeaves, +}; +typedef NSUInteger EJSONDeserializationOptions; + +@class CJSONScanner; + +@interface CJSONDeserializer : NSObject { + CJSONScanner *scanner; + EJSONDeserializationOptions options; +} + +@property (readwrite, nonatomic, retain) CJSONScanner *scanner; +/// Object to return instead when a null encountered in the JSON. Defaults to NSNull. Setting to null causes the scanner to skip null values. +@property (readwrite, nonatomic, retain) id nullObject; +/// JSON must be encoded in Unicode (UTF-8, UTF-16 or UTF-32). Use this if you expect to get the JSON in another encoding. +@property (readwrite, nonatomic, assign) NSStringEncoding allowedEncoding; +@property (readwrite, nonatomic, assign) EJSONDeserializationOptions options; + ++ (id)deserializer; + +- (id)deserialize:(NSData *)inData error:(NSError **)outError; + +- (id)deserializeAsDictionary:(NSData *)inData error:(NSError **)outError; +- (id)deserializeAsArray:(NSData *)inData error:(NSError **)outError; + +@end diff --git a/libs/TouchJSON/JSON/CJSONDeserializer.m b/libs/TouchJSON/JSON/CJSONDeserializer.m new file mode 100644 index 0000000..27a2d03 --- /dev/null +++ b/libs/TouchJSON/JSON/CJSONDeserializer.m @@ -0,0 +1,161 @@ +// +// CJSONDeserializer.m +// TouchCode +// +// Created by Jonathan Wight on 12/15/2005. +// Copyright 2005 toxicsoftware.com. All rights reserved. +// +// 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 "CJSONDeserializer.h" + +#import "CJSONScanner.h" +#import "CDataScanner.h" + +NSString *const kJSONDeserializerErrorDomain = @"CJSONDeserializerErrorDomain"; + +@interface CJSONDeserializer () +@end + +@implementation CJSONDeserializer + +@synthesize scanner; +@synthesize options; + ++ (id)deserializer + { + return([[[self alloc] init] autorelease]); + } + +- (id)init + { + if ((self = [super init]) != NULL) + { + } + return(self); + } + +- (void)dealloc + { + [scanner release]; + scanner = NULL; + // + [super dealloc]; + } + +#pragma mark - + +- (CJSONScanner *)scanner + { + if (scanner == NULL) + { + scanner = [[CJSONScanner alloc] init]; + } + return(scanner); + } + +- (id)nullObject + { + return(self.scanner.nullObject); + } + +- (void)setNullObject:(id)inNullObject + { + self.scanner.nullObject = inNullObject; + } + +#pragma mark - + +- (NSStringEncoding)allowedEncoding + { + return(self.scanner.allowedEncoding); + } + +- (void)setAllowedEncoding:(NSStringEncoding)inAllowedEncoding + { + self.scanner.allowedEncoding = inAllowedEncoding; + } + +#pragma mark - + +- (id)deserialize:(NSData *)inData error:(NSError **)outError + { + if (inData == NULL || [inData length] == 0) + { + if (outError) + *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL]; + + return(NULL); + } + if ([self.scanner setData:inData error:outError] == NO) + { + return(NULL); + } + id theObject = NULL; + if ([self.scanner scanJSONObject:&theObject error:outError] == YES) + return(theObject); + else + return(NULL); + } + +- (id)deserializeAsDictionary:(NSData *)inData error:(NSError **)outError + { + if (inData == NULL || [inData length] == 0) + { + if (outError) + *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL]; + + return(NULL); + } + if ([self.scanner setData:inData error:outError] == NO) + { + return(NULL); + } + NSDictionary *theDictionary = NULL; + if ([self.scanner scanJSONDictionary:&theDictionary error:outError] == YES) + return(theDictionary); + else + return(NULL); + } + +- (id)deserializeAsArray:(NSData *)inData error:(NSError **)outError + { + if (inData == NULL || [inData length] == 0) + { + if (outError) + *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL]; + + return(NULL); + } + if ([self.scanner setData:inData error:outError] == NO) + { + return(NULL); + } + NSArray *theArray = NULL; + if ([self.scanner scanJSONArray:&theArray error:outError] == YES) + return(theArray); + else + return(NULL); + } + +@end diff --git a/libs/TouchJSON/JSON/CJSONScanner.h b/libs/TouchJSON/JSON/CJSONScanner.h new file mode 100644 index 0000000..d410893 --- /dev/null +++ b/libs/TouchJSON/JSON/CJSONScanner.h @@ -0,0 +1,95 @@ +// +// CJSONScanner.h +// TouchCode +// +// Created by Jonathan Wight on 12/07/2005. +// Copyright 2005 toxicsoftware.com. All rights reserved. +// +// 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 "CDataScanner.h" + +enum { + kJSONScannerOptions_MutableContainers = 0x1, + kJSONScannerOptions_MutableLeaves = 0x2, +}; +typedef NSUInteger EJSONScannerOptions; + +/// CDataScanner subclass that understands JSON syntax natively. You should generally use CJSONDeserializer instead of this class. (TODO - this could have been a category?) +@interface CJSONScanner : CDataScanner { + BOOL strictEscapeCodes; + id nullObject; + NSStringEncoding allowedEncoding; + EJSONScannerOptions options; +} + +@property (readwrite, nonatomic, assign) BOOL strictEscapeCodes; +@property (readwrite, nonatomic, retain) id nullObject; +@property (readwrite, nonatomic, assign) NSStringEncoding allowedEncoding; +@property (readwrite, nonatomic, assign) EJSONScannerOptions options; + +- (BOOL)setData:(NSData *)inData error:(NSError **)outError; + +- (BOOL)scanJSONObject:(id *)outObject error:(NSError **)outError; +- (BOOL)scanJSONDictionary:(NSDictionary **)outDictionary error:(NSError **)outError; +- (BOOL)scanJSONArray:(NSArray **)outArray error:(NSError **)outError; +- (BOOL)scanJSONStringConstant:(NSString **)outStringConstant error:(NSError **)outError; +- (BOOL)scanJSONNumberConstant:(NSNumber **)outNumberConstant error:(NSError **)outError; + +@end + +extern NSString *const kJSONScannerErrorDomain /* = @"kJSONScannerErrorDomain" */; + +typedef enum { + + // Fundamental scanning errors + kJSONScannerErrorCode_NothingToScan = -11, + kJSONScannerErrorCode_CouldNotDecodeData = -12, + kJSONScannerErrorCode_CouldNotSerializeData = -13, + kJSONScannerErrorCode_CouldNotSerializeObject = -14, + kJSONScannerErrorCode_CouldNotScanObject = -15, + + // Dictionary scanning + kJSONScannerErrorCode_DictionaryStartCharacterMissing = -101, + kJSONScannerErrorCode_DictionaryKeyScanFailed = -102, + kJSONScannerErrorCode_DictionaryKeyNotTerminated = -103, + kJSONScannerErrorCode_DictionaryValueScanFailed = -104, + kJSONScannerErrorCode_DictionaryKeyValuePairNoDelimiter = -105, + kJSONScannerErrorCode_DictionaryNotTerminated = -106, + + // Array scanning + kJSONScannerErrorCode_ArrayStartCharacterMissing = -201, + kJSONScannerErrorCode_ArrayValueScanFailed = -202, + kJSONScannerErrorCode_ArrayValueIsNull = -203, + kJSONScannerErrorCode_ArrayNotTerminated = -204, + + // String scanning + kJSONScannerErrorCode_StringNotStartedWithBackslash = -301, + kJSONScannerErrorCode_StringUnicodeNotDecoded = -302, + kJSONScannerErrorCode_StringUnknownEscapeCode = -303, + kJSONScannerErrorCode_StringNotTerminated = -304, + + // Number scanning + kJSONScannerErrorCode_NumberNotScannable = -401 + +} EJSONScannerErrorCode; diff --git a/libs/TouchJSON/JSON/CJSONScanner.m b/libs/TouchJSON/JSON/CJSONScanner.m new file mode 100644 index 0000000..c5ffeb4 --- /dev/null +++ b/libs/TouchJSON/JSON/CJSONScanner.m @@ -0,0 +1,676 @@ +// +// CJSONScanner.m +// TouchCode +// +// Created by Jonathan Wight on 12/07/2005. +// Copyright 2005 toxicsoftware.com. All rights reserved. +// +// 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 "CJSONScanner.h" + +#import "CDataScanner_Extensions.h" + +#if !defined(TREAT_COMMENTS_AS_WHITESPACE) +#define TREAT_COMMENTS_AS_WHITESPACE 0 +#endif // !defined(TREAT_COMMENTS_AS_WHITESPACE) + +NSString *const kJSONScannerErrorDomain = @"kJSONScannerErrorDomain"; + +inline static int HexToInt(char inCharacter) + { + int theValues[] = { 0x0 /* 48 '0' */, 0x1 /* 49 '1' */, 0x2 /* 50 '2' */, 0x3 /* 51 '3' */, 0x4 /* 52 '4' */, 0x5 /* 53 '5' */, 0x6 /* 54 '6' */, 0x7 /* 55 '7' */, 0x8 /* 56 '8' */, 0x9 /* 57 '9' */, -1 /* 58 ':' */, -1 /* 59 ';' */, -1 /* 60 '<' */, -1 /* 61 '=' */, -1 /* 62 '>' */, -1 /* 63 '?' */, -1 /* 64 '@' */, 0xa /* 65 'A' */, 0xb /* 66 'B' */, 0xc /* 67 'C' */, 0xd /* 68 'D' */, 0xe /* 69 'E' */, 0xf /* 70 'F' */, -1 /* 71 'G' */, -1 /* 72 'H' */, -1 /* 73 'I' */, -1 /* 74 'J' */, -1 /* 75 'K' */, -1 /* 76 'L' */, -1 /* 77 'M' */, -1 /* 78 'N' */, -1 /* 79 'O' */, -1 /* 80 'P' */, -1 /* 81 'Q' */, -1 /* 82 'R' */, -1 /* 83 'S' */, -1 /* 84 'T' */, -1 /* 85 'U' */, -1 /* 86 'V' */, -1 /* 87 'W' */, -1 /* 88 'X' */, -1 /* 89 'Y' */, -1 /* 90 'Z' */, -1 /* 91 '[' */, -1 /* 92 '\' */, -1 /* 93 ']' */, -1 /* 94 '^' */, -1 /* 95 '_' */, -1 /* 96 '`' */, 0xa /* 97 'a' */, 0xb /* 98 'b' */, 0xc /* 99 'c' */, 0xd /* 100 'd' */, 0xe /* 101 'e' */, 0xf /* 102 'f' */, }; + if (inCharacter >= '0' && inCharacter <= 'f') + return(theValues[inCharacter - '0']); + else + return(-1); + } + +@interface CJSONScanner () +- (BOOL)scanNotQuoteCharactersIntoString:(NSString **)outValue; +@end + +#pragma mark - + +@implementation CJSONScanner + +@synthesize strictEscapeCodes; +@synthesize nullObject; +@synthesize allowedEncoding; +@synthesize options; + +- (id)init + { + if ((self = [super init]) != NULL) + { + strictEscapeCodes = NO; + nullObject = [[NSNull null] retain]; + } + return(self); + } + +- (void)dealloc + { + [nullObject release]; + nullObject = NULL; + // + [super dealloc]; + } + +#pragma mark - + +- (BOOL)setData:(NSData *)inData error:(NSError **)outError; + { + NSData *theData = inData; + if (theData && theData.length >= 4) + { + // This code is lame, but it works. Because the first character of any JSON string will always be a (ascii) control character we can work out the Unicode encoding by the bit pattern. See section 3 of http://www.ietf.org/rfc/rfc4627.txt + const char *theChars = theData.bytes; + NSStringEncoding theEncoding = NSUTF8StringEncoding; + if (theChars[0] != 0 && theChars[1] == 0) + { + if (theChars[2] != 0 && theChars[3] == 0) + theEncoding = NSUTF16LittleEndianStringEncoding; + else if (theChars[2] == 0 && theChars[3] == 0) + theEncoding = NSUTF32LittleEndianStringEncoding; + } + else if (theChars[0] == 0 && theChars[2] == 0 && theChars[3] != 0) + { + if (theChars[1] == 0) + theEncoding = NSUTF32BigEndianStringEncoding; + else if (theChars[1] != 0) + theEncoding = NSUTF16BigEndianStringEncoding; + } + + NSString *theString = [[NSString alloc] initWithData:theData encoding:theEncoding]; + if (theString == NULL && self.allowedEncoding != 0) + { + theString = [[NSString alloc] initWithData:theData encoding:self.allowedEncoding]; + } + theData = [theString dataUsingEncoding:NSUTF8StringEncoding]; + [theString release]; + } + + if (theData) + { + [super setData:theData]; + return(YES); + } + else + { + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan data. Data wasn't encoded properly?", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_CouldNotDecodeData userInfo:theUserInfo]; + } + return(NO); + } + } + +- (void)setData:(NSData *)inData + { + [self setData:inData error:NULL]; + } + +#pragma mark - + +- (BOOL)scanJSONObject:(id *)outObject error:(NSError **)outError + { + BOOL theResult = YES; + + [self skipWhitespace]; + + id theObject = NULL; + + const unichar C = [self currentCharacter]; + switch (C) + { + case 't': + if ([self scanUTF8String:"true" intoString:NULL]) + { + theObject = [NSNumber numberWithBool:YES]; + } + break; + case 'f': + if ([self scanUTF8String:"false" intoString:NULL]) + { + theObject = [NSNumber numberWithBool:NO]; + } + break; + case 'n': + if ([self scanUTF8String:"null" intoString:NULL]) + { + theObject = self.nullObject; + } + break; + case '\"': + case '\'': + theResult = [self scanJSONStringConstant:&theObject error:outError]; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + theResult = [self scanJSONNumberConstant:&theObject error:outError]; + break; + case '{': + theResult = [self scanJSONDictionary:&theObject error:outError]; + break; + case '[': + theResult = [self scanJSONArray:&theObject error:outError]; + break; + default: + theResult = NO; + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan object. Character not a valid JSON character.", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_CouldNotScanObject userInfo:theUserInfo]; + } + break; + } + + if (outObject != NULL) + *outObject = theObject; + + return(theResult); + } + +- (BOOL)scanJSONDictionary:(NSDictionary **)outDictionary error:(NSError **)outError + { + NSUInteger theScanLocation = [self scanLocation]; + + [self skipWhitespace]; + + if ([self scanCharacter:'{'] == NO) + { + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan dictionary. Dictionary that does not start with '{' character.", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryStartCharacterMissing userInfo:theUserInfo]; + } + return(NO); + } + + NSMutableDictionary *theDictionary = [[NSMutableDictionary alloc] init]; + + while ([self currentCharacter] != '}') + { + [self skipWhitespace]; + + if ([self currentCharacter] == '}') + break; + + NSString *theKey = NULL; + if ([self scanJSONStringConstant:&theKey error:outError] == NO) + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan dictionary. Failed to scan a key.", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyScanFailed userInfo:theUserInfo]; + } + [theDictionary release]; + return(NO); + } + + [self skipWhitespace]; + + if ([self scanCharacter:':'] == NO) + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan dictionary. Key was not terminated with a ':' character.", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyNotTerminated userInfo:theUserInfo]; + } + [theDictionary release]; + return(NO); + } + + id theValue = NULL; + if ([self scanJSONObject:&theValue error:outError] == NO) + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan dictionary. Failed to scan a value.", NSLocalizedDescriptionKey, + NULL]; + + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryValueScanFailed userInfo:theUserInfo]; + } + [theDictionary release]; + return(NO); + } + + if (theValue == NULL && self.nullObject == NULL) + { + // If the value is a null and nullObject is also null then we're skipping this key/value pair. + } + else + { + [theDictionary setValue:theValue forKey:theKey]; + } + + [self skipWhitespace]; + if ([self scanCharacter:','] == NO) + { + if ([self currentCharacter] != '}') + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan dictionary. Key value pairs not delimited with a ',' character.", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyValuePairNoDelimiter userInfo:theUserInfo]; + } + [theDictionary release]; + return(NO); + } + break; + } + else + { + [self skipWhitespace]; + if ([self currentCharacter] == '}') + break; + } + } + + if ([self scanCharacter:'}'] == NO) + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan dictionary. Dictionary not terminated by a '}' character.", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryNotTerminated userInfo:theUserInfo]; + } + [theDictionary release]; + return(NO); + } + + if (outDictionary != NULL) + { + if (self.options & kJSONScannerOptions_MutableContainers) + { + *outDictionary = [theDictionary autorelease]; + } + else + { + *outDictionary = [[theDictionary copy] autorelease]; + [theDictionary release]; + } + } + else + { + [theDictionary release]; + } + + return(YES); + } + +- (BOOL)scanJSONArray:(NSArray **)outArray error:(NSError **)outError + { + NSUInteger theScanLocation = [self scanLocation]; + + [self skipWhitespace]; + + if ([self scanCharacter:'['] == NO) + { + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan array. Array not started by a '[' character.", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayStartCharacterMissing userInfo:theUserInfo]; + } + return(NO); + } + + NSMutableArray *theArray = [[NSMutableArray alloc] init]; + + [self skipWhitespace]; + while ([self currentCharacter] != ']') + { + NSString *theValue = NULL; + if ([self scanJSONObject:&theValue error:outError] == NO) + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan array. Could not scan a value.", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayValueScanFailed userInfo:theUserInfo]; + } + [theArray release]; + return(NO); + } + + if (theValue == NULL) + { + if (self.nullObject != NULL) + { + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan array. Value is NULL.", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayValueIsNull userInfo:theUserInfo]; + } + [theArray release]; + return(NO); + } + } + else + { + [theArray addObject:theValue]; + } + + [self skipWhitespace]; + if ([self scanCharacter:','] == NO) + { + [self skipWhitespace]; + if ([self currentCharacter] != ']') + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan array. Array not terminated by a ']' character.", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayNotTerminated userInfo:theUserInfo]; + } + [theArray release]; + return(NO); + } + + break; + } + [self skipWhitespace]; + } + + [self skipWhitespace]; + + if ([self scanCharacter:']'] == NO) + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan array. Array not terminated by a ']' character.", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayNotTerminated userInfo:theUserInfo]; + } + [theArray release]; + return(NO); + } + + if (outArray != NULL) + { + if (self.options & kJSONScannerOptions_MutableContainers) + { + *outArray = [theArray autorelease]; + } + else + { + *outArray = [[theArray copy] autorelease]; + [theArray release]; + } + } + else + { + [theArray release]; + } + return(YES); + } + +- (BOOL)scanJSONStringConstant:(NSString **)outStringConstant error:(NSError **)outError + { + NSUInteger theScanLocation = [self scanLocation]; + + [self skipWhitespace]; + + NSMutableString *theString = [[NSMutableString alloc] init]; + + if ([self scanCharacter:'"'] == NO) + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan string constant. String not started by a '\"' character.", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringNotStartedWithBackslash userInfo:theUserInfo]; + } + [theString release]; + return(NO); + } + + while ([self scanCharacter:'"'] == NO) + { + NSString *theStringChunk = NULL; + if ([self scanNotQuoteCharactersIntoString:&theStringChunk]) + { + CFStringAppend((CFMutableStringRef)theString, (CFStringRef)theStringChunk); + } + else if ([self scanCharacter:'\\'] == YES) + { + unichar theCharacter = [self scanCharacter]; + switch (theCharacter) + { + case '"': + case '\\': + case '/': + break; + case 'b': + theCharacter = '\b'; + break; + case 'f': + theCharacter = '\f'; + break; + case 'n': + theCharacter = '\n'; + break; + case 'r': + theCharacter = '\r'; + break; + case 't': + theCharacter = '\t'; + break; + case 'u': + { + theCharacter = 0; + + int theShift; + for (theShift = 12; theShift >= 0; theShift -= 4) + { + const int theDigit = HexToInt([self scanCharacter]); + if (theDigit == -1) + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan string constant. Unicode character could not be decoded.", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringUnicodeNotDecoded userInfo:theUserInfo]; + } + [theString release]; + return(NO); + } + theCharacter |= (theDigit << theShift); + } + } + break; + default: + { + if (strictEscapeCodes == YES) + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan string constant. Unknown escape code.", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringUnknownEscapeCode userInfo:theUserInfo]; + } + [theString release]; + return(NO); + } + } + break; + } + CFStringAppendCharacters((CFMutableStringRef)theString, &theCharacter, 1); + } + else + { + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan string constant. No terminating double quote character.", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringNotTerminated userInfo:theUserInfo]; + } + [theString release]; + return(NO); + } + } + + if (outStringConstant != NULL) + { + if (self.options & kJSONScannerOptions_MutableLeaves) + { + *outStringConstant = [theString autorelease]; + } + else + { + *outStringConstant = [[theString copy] autorelease]; + [theString release]; + } + } + else + { + [theString release]; + } + + return(YES); + } + +- (BOOL)scanJSONNumberConstant:(NSNumber **)outNumberConstant error:(NSError **)outError + { + NSNumber *theNumber = NULL; + + [self skipWhitespace]; + + if ([self scanNumber:&theNumber] == YES) + { + if (outNumberConstant != NULL) + *outNumberConstant = theNumber; + return(YES); + } + else + { + if (outError) + { + NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"Could not scan number constant.", NSLocalizedDescriptionKey, + NULL]; + [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_NumberNotScannable userInfo:theUserInfo]; + } + return(NO); + } + } + +#if TREAT_COMMENTS_AS_WHITESPACE +- (void)skipWhitespace + { + [super skipWhitespace]; + [self scanCStyleComment:NULL]; + [self scanCPlusPlusStyleComment:NULL]; + [super skipWhitespace]; + } +#endif // TREAT_COMMENTS_AS_WHITESPACE + +#pragma mark - + +- (BOOL)scanNotQuoteCharactersIntoString:(NSString **)outValue + { + u_int8_t *P; + for (P = current; P < end && *P != '\"' && *P != '\\'; ++P) + ; + + if (P == current) + { + return(NO); + } + + if (outValue) + { + *outValue = [[[NSString alloc] initWithBytes:current length:P - current encoding:NSUTF8StringEncoding] autorelease]; + } + + current = P; + + return(YES); + } + +@end diff --git a/libs/TouchJSON/JSON/CJSONSerializer.h b/libs/TouchJSON/JSON/CJSONSerializer.h new file mode 100644 index 0000000..748a85c --- /dev/null +++ b/libs/TouchJSON/JSON/CJSONSerializer.h @@ -0,0 +1,53 @@ +// +// CJSONSerializer.h +// TouchCode +// +// Created by Jonathan Wight on 12/07/2005. +// Copyright 2005 toxicsoftware.com. All rights reserved. +// +// 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 + +@interface CJSONSerializer : NSObject { +} + ++ (id)serializer; + +- (BOOL)isValidJSONObject:(id)inObject; + +/// Take any JSON compatible object (generally NSNull, NSNumber, NSString, NSArray and NSDictionary) and produce an NSData containing the serialized JSON. +- (NSData *)serializeObject:(id)inObject error:(NSError **)outError; + +- (NSData *)serializeNull:(NSNull *)inNull error:(NSError **)outError; +- (NSData *)serializeNumber:(NSNumber *)inNumber error:(NSError **)outError; +- (NSData *)serializeString:(NSString *)inString error:(NSError **)outError; +- (NSData *)serializeArray:(NSArray *)inArray error:(NSError **)outError; +- (NSData *)serializeDictionary:(NSDictionary *)inDictionary error:(NSError **)outError; + +@end + +typedef enum { + CJSONSerializerErrorCouldNotSerializeDataType = -1, + CJSONSerializerErrorCouldNotSerializeObject = -1 +} CJSONSerializerError; diff --git a/libs/TouchJSON/JSON/CJSONSerializer.m b/libs/TouchJSON/JSON/CJSONSerializer.m new file mode 100644 index 0000000..952b3c2 --- /dev/null +++ b/libs/TouchJSON/JSON/CJSONSerializer.m @@ -0,0 +1,342 @@ +// +// CJSONSerializer.m +// TouchCode +// +// Created by Jonathan Wight on 12/07/2005. +// Copyright 2005 toxicsoftware.com. All rights reserved. +// +// 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 "CJSONSerializer.h" + +#import "JSONRepresentation.h" + +static NSData *kNULL = NULL; +static NSData *kFalse = NULL; +static NSData *kTrue = NULL; + +@implementation CJSONSerializer + ++ (void)initialize + { + NSAutoreleasePool *thePool = [[NSAutoreleasePool alloc] init]; + + if (self == [CJSONSerializer class]) + { + if (kNULL == NULL) + kNULL = [[NSData alloc] initWithBytesNoCopy:(void *)"null" length:4 freeWhenDone:NO]; + if (kFalse == NULL) + kFalse = [[NSData alloc] initWithBytesNoCopy:(void *)"false" length:5 freeWhenDone:NO]; + if (kTrue == NULL) + kTrue = [[NSData alloc] initWithBytesNoCopy:(void *)"true" length:4 freeWhenDone:NO]; + + [thePool release]; + } + } + ++ (id)serializer + { + return([[[self alloc] init] autorelease]); + } + +- (BOOL)isValidJSONObject:(id)inObject + { + if ([inObject isKindOfClass:[NSNull class]]) + { + return(YES); + } + else if ([inObject isKindOfClass:[NSNumber class]]) + { + return(YES); + } + else if ([inObject isKindOfClass:[NSString class]]) + { + return(YES); + } + else if ([inObject isKindOfClass:[NSArray class]]) + { + return(YES); + } + else if ([inObject isKindOfClass:[NSDictionary class]]) + { + return(YES); + } + else if ([inObject isKindOfClass:[NSData class]]) + { + return(YES); + } + else if ([inObject respondsToSelector:@selector(JSONDataRepresentation)]) + { + return(YES); + } + else + { + return(NO); + } + } + +- (NSData *)serializeObject:(id)inObject error:(NSError **)outError + { + NSData *theResult = NULL; + + if ([inObject isKindOfClass:[NSNull class]]) + { + theResult = [self serializeNull:inObject error:outError]; + } + else if ([inObject isKindOfClass:[NSNumber class]]) + { + theResult = [self serializeNumber:inObject error:outError]; + } + else if ([inObject isKindOfClass:[NSString class]]) + { + theResult = [self serializeString:inObject error:outError]; + } + else if ([inObject isKindOfClass:[NSArray class]]) + { + theResult = [self serializeArray:inObject error:outError]; + } + else if ([inObject isKindOfClass:[NSDictionary class]]) + { + theResult = [self serializeDictionary:inObject error:outError]; + } + else if ([inObject isKindOfClass:[NSData class]]) + { + NSString *theString = [[[NSString alloc] initWithData:inObject encoding:NSUTF8StringEncoding] autorelease]; + theResult = [self serializeString:theString error:outError]; + } + else if ([inObject respondsToSelector:@selector(JSONDataRepresentation)]) + { + theResult = [inObject JSONDataRepresentation]; + } + else + { + if (outError) + { + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithFormat:@"Cannot serialize data of type '%@'", NSStringFromClass([inObject class])], NSLocalizedDescriptionKey, + NULL]; + *outError = [NSError errorWithDomain:@"TODO_DOMAIN" code:CJSONSerializerErrorCouldNotSerializeDataType userInfo:theUserInfo]; + } + return(NULL); + } + if (theResult == NULL) + { + if (outError) + { + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithFormat:@"Could not serialize object '%@'", inObject], NSLocalizedDescriptionKey, + NULL]; + *outError = [NSError errorWithDomain:@"TODO_DOMAIN" code:CJSONSerializerErrorCouldNotSerializeObject userInfo:theUserInfo]; + } + return(NULL); + } + return(theResult); + } + +- (NSData *)serializeNull:(NSNull *)inNull error:(NSError **)outError + { + #pragma unused (inNull, outError) + return(kNULL); + } + +- (NSData *)serializeNumber:(NSNumber *)inNumber error:(NSError **)outError + { + #pragma unused (outError) + NSData *theResult = NULL; + switch (CFNumberGetType((CFNumberRef)inNumber)) + { + case kCFNumberCharType: + { + int theValue = [inNumber intValue]; + if (theValue == 0) + theResult = kFalse; + else if (theValue == 1) + theResult = kTrue; + else + theResult = [[inNumber stringValue] dataUsingEncoding:NSASCIIStringEncoding]; + } + break; + case kCFNumberFloat32Type: + case kCFNumberFloat64Type: + case kCFNumberFloatType: + case kCFNumberDoubleType: + case kCFNumberSInt8Type: + case kCFNumberSInt16Type: + case kCFNumberSInt32Type: + case kCFNumberSInt64Type: + case kCFNumberShortType: + case kCFNumberIntType: + case kCFNumberLongType: + case kCFNumberLongLongType: + case kCFNumberCFIndexType: + default: + theResult = [[inNumber stringValue] dataUsingEncoding:NSASCIIStringEncoding]; + break; + } + return(theResult); + } + +- (NSData *)serializeString:(NSString *)inString error:(NSError **)outError + { + #pragma unused (outError) + + const char *theUTF8String = [inString UTF8String]; + + NSMutableData *theData = [NSMutableData dataWithLength:strlen(theUTF8String) * 2 + 2]; + + char *theOutputStart = [theData mutableBytes]; + char *OUT = theOutputStart; + + *OUT++ = '"'; + + for (const char *IN = theUTF8String; IN && *IN != '\0'; ++IN) + { + switch (*IN) + { + case '\\': + { + *OUT++ = '\\'; + *OUT++ = '\\'; + } + break; + case '\"': + { + *OUT++ = '\\'; + *OUT++ = '\"'; + } + break; + case '/': + { + *OUT++ = '\\'; + *OUT++ = '/'; + } + break; + case '\b': + { + *OUT++ = '\\'; + *OUT++ = 'b'; + } + break; + case '\f': + { + *OUT++ = '\\'; + *OUT++ = 'f'; + } + break; + case '\n': + { + *OUT++ = '\\'; + *OUT++ = 'n'; + } + break; + case '\r': + { + *OUT++ = '\\'; + *OUT++ = 'r'; + } + break; + case '\t': + { + *OUT++ = '\\'; + *OUT++ = 't'; + } + break; + default: + { + *OUT++ = *IN; + } + break; + } + } + + *OUT++ = '"'; + + theData.length = OUT - theOutputStart; + return(theData); + } + +- (NSData *)serializeArray:(NSArray *)inArray error:(NSError **)outError + { + NSMutableData *theData = [NSMutableData data]; + + [theData appendBytes:"[" length:1]; + + NSEnumerator *theEnumerator = [inArray objectEnumerator]; + id theValue = NULL; + NSUInteger i = 0; + while ((theValue = [theEnumerator nextObject]) != NULL) + { + NSData *theValueData = [self serializeObject:theValue error:outError]; + if (theValueData == NULL) + { + return(NULL); + } + [theData appendData:theValueData]; + if (++i < [inArray count]) + [theData appendBytes:"," length:1]; + } + + [theData appendBytes:"]" length:1]; + + return(theData); + } + +- (NSData *)serializeDictionary:(NSDictionary *)inDictionary error:(NSError **)outError + { + NSMutableData *theData = [NSMutableData data]; + + [theData appendBytes:"{" length:1]; + + NSArray *theKeys = [inDictionary allKeys]; + NSEnumerator *theEnumerator = [theKeys objectEnumerator]; + NSString *theKey = NULL; + while ((theKey = [theEnumerator nextObject]) != NULL) + { + id theValue = [inDictionary objectForKey:theKey]; + + NSData *theKeyData = [self serializeString:theKey error:outError]; + if (theKeyData == NULL) + { + return(NULL); + } + NSData *theValueData = [self serializeObject:theValue error:outError]; + if (theValueData == NULL) + { + return(NULL); + } + + + [theData appendData:theKeyData]; + [theData appendBytes:":" length:1]; + [theData appendData:theValueData]; + + if (theKey != [theKeys lastObject]) + [theData appendData:[@"," dataUsingEncoding:NSASCIIStringEncoding]]; + } + + [theData appendBytes:"}" length:1]; + + return(theData); + } + +@end diff --git a/libs/TouchJSON/JSON/JSONRepresentation.h b/libs/TouchJSON/JSON/JSONRepresentation.h new file mode 100644 index 0000000..a83d76f --- /dev/null +++ b/libs/TouchJSON/JSON/JSONRepresentation.h @@ -0,0 +1,18 @@ +// +// JSONRepresentation.h +// TouchJSON +// +// Created by Jonathan Wight on 10/15/10. +// Copyright 2010 toxicsoftware.com. All rights reserved. +// + +#import + +@protocol JSONRepresentation + +@optional +- (id)initWithJSONDataRepresentation:(NSData *)inJSONData; + +- (NSData *)JSONDataRepresentation; + +@end diff --git a/libs/cocos2d/CCAction.h b/libs/cocos2d/CCAction.h new file mode 100644 index 0000000..5d02656 --- /dev/null +++ b/libs/cocos2d/CCAction.h @@ -0,0 +1,194 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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. + * + */ + + +#include +#import + +#import "ccTypes.h" + +enum { + //! Default tag + kCCActionTagInvalid = -1, +}; + +/** Base class for CCAction objects. + */ +@interface CCAction : NSObject +{ + id originalTarget_; + id target_; + NSInteger tag_; +} + +/** The "target". The action will modify the target properties. + The target will be set with the 'startWithTarget' method. + When the 'stop' method is called, target will be set to nil. + The target is 'assigned', it is not 'retained'. + */ +@property (nonatomic,readonly,assign) id target; + +/** The original target, since target can be nil. + Is the target that were used to run the action. Unless you are doing something complex, like CCActionManager, you should NOT call this method. + @since v0.8.2 +*/ +@property (nonatomic,readonly,assign) id originalTarget; + + +/** The action tag. An identifier of the action */ +@property (nonatomic,readwrite,assign) NSInteger tag; + +/** Allocates and initializes the action */ ++(id) action; + +/** Initializes the action */ +-(id) init; + +-(id) copyWithZone: (NSZone*) zone; + +//! return YES if the action has finished +-(BOOL) isDone; +//! called before the action start. It will also set the target. +-(void) startWithTarget:(id)target; +//! called after the action has finished. It will set the 'target' to nil. +//! IMPORTANT: You should never call "[action stop]" manually. Instead, use: "[target stopAction:action];" +-(void) stop; +//! called every frame with it's delta time. DON'T override unless you know what you are doing. +-(void) step: (ccTime) dt; +//! called once per frame. time a value between 0 and 1 +//! For example: +//! * 0 means that the action just started +//! * 0.5 means that the action is in the middle +//! * 1 means that the action is over +-(void) update: (ccTime) time; + +@end + +/** Base class actions that do have a finite time duration. + Possible actions: + - An action with a duration of 0 seconds + - An action with a duration of 35.5 seconds + Infitite time actions are valid + */ +@interface CCFiniteTimeAction : CCAction +{ + //! duration in seconds + ccTime duration_; +} +//! duration in seconds of the action +@property (nonatomic,readwrite) ccTime duration; + +/** returns a reversed action */ +- (CCFiniteTimeAction*) reverse; +@end + + +@class CCActionInterval; +/** Repeats an action for ever. + To repeat the an action for a limited number of times use the Repeat action. + @warning This action can't be Sequenceable because it is not an IntervalAction + */ +@interface CCRepeatForever : CCAction +{ + CCActionInterval *innerAction_; +} +/** Inner action */ +@property (nonatomic, readwrite, retain) CCActionInterval *innerAction; + +/** creates the action */ ++(id) actionWithAction: (CCActionInterval*) action; +/** initializes the action */ +-(id) initWithAction: (CCActionInterval*) action; +@end + +/** Changes the speed of an action, making it take longer (speed>1) + or less (speed<1) time. + Useful to simulate 'slow motion' or 'fast forward' effect. + @warning This action can't be Sequenceable because it is not an CCIntervalAction + */ +@interface CCSpeed : CCAction +{ + CCActionInterval *innerAction_; + float speed_; +} +/** alter the speed of the inner function in runtime */ +@property (nonatomic,readwrite) float speed; +/** Inner action of CCSpeed */ +@property (nonatomic, readwrite, retain) CCActionInterval *innerAction; + +/** creates the action */ ++(id) actionWithAction: (CCActionInterval*) action speed:(float)rate; +/** initializes the action */ +-(id) initWithAction: (CCActionInterval*) action speed:(float)rate; +@end + +@class CCNode; +/** CCFollow is an action that "follows" a node. + + Eg: + [layer runAction: [CCFollow actionWithTarget:hero]]; + + Instead of using CCCamera as a "follower", use this action instead. + @since v0.99.2 + */ +@interface CCFollow : CCAction +{ + /* node to follow */ + CCNode *followedNode_; + + /* whether camera should be limited to certain area */ + BOOL boundarySet; + + /* if screensize is bigger than the boundary - update not needed */ + BOOL boundaryFullyCovered; + + /* fast access to the screen dimensions */ + CGPoint halfScreenSize; + CGPoint fullScreenSize; + + /* world boundaries */ + float leftBoundary; + float rightBoundary; + float topBoundary; + float bottomBoundary; +} + +/** alter behavior - turn on/off boundary */ +@property (nonatomic,readwrite) BOOL boundarySet; + +/** creates the action with no boundary set */ ++(id) actionWithTarget:(CCNode *)followedNode; + +/** creates the action with a set boundary */ ++(id) actionWithTarget:(CCNode *)followedNode worldBoundary:(CGRect)rect; + +/** initializes the action */ +-(id) initWithTarget:(CCNode *)followedNode; + +/** initializes the action with a set boundary */ +-(id) initWithTarget:(CCNode *)followedNode worldBoundary:(CGRect)rect; + +@end + diff --git a/libs/cocos2d/CCAction.m b/libs/cocos2d/CCAction.m new file mode 100644 index 0000000..69fd47e --- /dev/null +++ b/libs/cocos2d/CCAction.m @@ -0,0 +1,361 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 +#import "CCDirector.h" +#import "ccMacros.h" +#import "CCAction.h" +#import "CCActionInterval.h" +#import "Support/CGPointExtension.h" + +// +// Action Base Class +// +#pragma mark - +#pragma mark Action +@implementation CCAction + +@synthesize tag = tag_, target = target_, originalTarget = originalTarget_; + ++(id) action +{ + return [[[self alloc] init] autorelease]; +} + +-(id) init +{ + if( (self=[super init]) ) { + originalTarget_ = target_ = nil; + tag_ = kCCActionTagInvalid; + } + return self; +} + +-(void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + [super dealloc]; +} + +-(NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag_]; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] init]; + copy.tag = tag_; + return copy; +} + +-(void) startWithTarget:(id)aTarget +{ + originalTarget_ = target_ = aTarget; +} + +-(void) stop +{ + target_ = nil; +} + +-(BOOL) isDone +{ + return YES; +} + +-(void) step: (ccTime) dt +{ + NSLog(@"[Action step]. override me"); +} + +-(void) update: (ccTime) time +{ + NSLog(@"[Action update]. override me"); +} +@end + +// +// FiniteTimeAction +// +#pragma mark - +#pragma mark FiniteTimeAction +@implementation CCFiniteTimeAction +@synthesize duration = duration_; + +- (CCFiniteTimeAction*) reverse +{ + CCLOG(@"cocos2d: FiniteTimeAction#reverse: Implement me"); + return nil; +} +@end + + +// +// RepeatForever +// +#pragma mark - +#pragma mark RepeatForever +@implementation CCRepeatForever +@synthesize innerAction=innerAction_; ++(id) actionWithAction: (CCActionInterval*) action +{ + return [[[self alloc] initWithAction: action] autorelease]; +} + +-(id) initWithAction: (CCActionInterval*) action +{ + if( (self=[super init]) ) + self.innerAction = action; + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] initWithAction:[[innerAction_ copy] autorelease] ]; + return copy; +} + +-(void) dealloc +{ + [innerAction_ release]; + [super dealloc]; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + [innerAction_ startWithTarget:target_]; +} + +-(void) step:(ccTime) dt +{ + [innerAction_ step: dt]; + if( [innerAction_ isDone] ) { + ccTime diff = dt + innerAction_.duration - innerAction_.elapsed; + [innerAction_ startWithTarget:target_]; + + // to prevent jerk. issue #390 + [innerAction_ step: diff]; + } +} + + +-(BOOL) isDone +{ + return NO; +} + +- (CCActionInterval *) reverse +{ + return [CCRepeatForever actionWithAction:[innerAction_ reverse]]; +} +@end + +// +// Speed +// +#pragma mark - +#pragma mark Speed +@implementation CCSpeed +@synthesize speed=speed_; +@synthesize innerAction=innerAction_; + ++(id) actionWithAction: (CCActionInterval*) action speed:(float)r +{ + return [[[self alloc] initWithAction: action speed:r] autorelease]; +} + +-(id) initWithAction: (CCActionInterval*) action speed:(float)r +{ + if( (self=[super init]) ) { + self.innerAction = action; + speed_ = r; + } + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] initWithAction:[[innerAction_ copy] autorelease] speed:speed_]; + return copy; +} + +-(void) dealloc +{ + [innerAction_ release]; + [super dealloc]; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + [innerAction_ startWithTarget:target_]; +} + +-(void) stop +{ + [innerAction_ stop]; + [super stop]; +} + +-(void) step:(ccTime) dt +{ + [innerAction_ step: dt * speed_]; +} + +-(BOOL) isDone +{ + return [innerAction_ isDone]; +} + +- (CCActionInterval *) reverse +{ + return [CCSpeed actionWithAction:[innerAction_ reverse] speed:speed_]; +} +@end + +// +// Follow +// +#pragma mark - +#pragma mark Follow +@implementation CCFollow + +@synthesize boundarySet; + ++(id) actionWithTarget:(CCNode *) fNode +{ + return [[[self alloc] initWithTarget:fNode] autorelease]; +} + ++(id) actionWithTarget:(CCNode *) fNode worldBoundary:(CGRect)rect +{ + return [[[self alloc] initWithTarget:fNode worldBoundary:rect] autorelease]; +} + +-(id) initWithTarget:(CCNode *)fNode +{ + if( (self=[super init]) ) { + + followedNode_ = [fNode retain]; + boundarySet = FALSE; + boundaryFullyCovered = FALSE; + + CGSize s = [[CCDirector sharedDirector] winSize]; + fullScreenSize = CGPointMake(s.width, s.height); + halfScreenSize = ccpMult(fullScreenSize, .5f); + } + + return self; +} + +-(id) initWithTarget:(CCNode *)fNode worldBoundary:(CGRect)rect +{ + if( (self=[super init]) ) { + + followedNode_ = [fNode retain]; + boundarySet = TRUE; + boundaryFullyCovered = FALSE; + + CGSize winSize = [[CCDirector sharedDirector] winSize]; + fullScreenSize = CGPointMake(winSize.width, winSize.height); + halfScreenSize = ccpMult(fullScreenSize, .5f); + + leftBoundary = -((rect.origin.x+rect.size.width) - fullScreenSize.x); + rightBoundary = -rect.origin.x ; + topBoundary = -rect.origin.y; + bottomBoundary = -((rect.origin.y+rect.size.height) - fullScreenSize.y); + + if(rightBoundary < leftBoundary) + { + // screen width is larger than world's boundary width + //set both in the middle of the world + rightBoundary = leftBoundary = (leftBoundary + rightBoundary) / 2; + } + if(topBoundary < bottomBoundary) + { + // screen width is larger than world's boundary width + //set both in the middle of the world + topBoundary = bottomBoundary = (topBoundary + bottomBoundary) / 2; + } + + if( (topBoundary == bottomBoundary) && (leftBoundary == rightBoundary) ) + boundaryFullyCovered = TRUE; + } + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] init]; + copy.tag = tag_; + return copy; +} + +-(void) step:(ccTime) dt +{ +#define CLAMP(x,y,z) MIN(MAX(x,y),z) + + if(boundarySet) + { + // whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased + if(boundaryFullyCovered) + return; + + CGPoint tempPos = ccpSub( halfScreenSize, followedNode_.position); + [target_ setPosition:ccp(CLAMP(tempPos.x,leftBoundary,rightBoundary), CLAMP(tempPos.y,bottomBoundary,topBoundary))]; + } + else + [target_ setPosition:ccpSub( halfScreenSize, followedNode_.position )]; + +#undef CLAMP +} + + +-(BOOL) isDone +{ + return !followedNode_.isRunning; +} + +-(void) stop +{ + target_ = nil; + [super stop]; +} + +-(void) dealloc +{ + [followedNode_ release]; + [super dealloc]; +} + +@end + + diff --git a/libs/cocos2d/CCActionCamera.h b/libs/cocos2d/CCActionCamera.h new file mode 100644 index 0000000..131c084 --- /dev/null +++ b/libs/cocos2d/CCActionCamera.h @@ -0,0 +1,72 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CCActionInterval.h" + +@class CCCamera; + +/** Base class for CCCamera actions + */ +@interface CCActionCamera : CCActionInterval +{ + float centerXOrig_; + float centerYOrig_; + float centerZOrig_; + + float eyeXOrig_; + float eyeYOrig_; + float eyeZOrig_; + + float upXOrig_; + float upYOrig_; + float upZOrig_; +} +@end + +/** CCOrbitCamera action + Orbits the camera around the center of the screen using spherical coordinates + */ +@interface CCOrbitCamera : CCActionCamera +{ + float radius_; + float deltaRadius_; + float angleZ_; + float deltaAngleZ_; + float angleX_; + float deltaAngleX_; + + float radZ_; + float radDeltaZ_; + float radX_; + float radDeltaX_; + +} +/** creates a CCOrbitCamera action with radius, delta-radius, z, deltaZ, x, deltaX */ ++(id) actionWithDuration:(float) t radius:(float)r deltaRadius:(float) dr angleZ:(float)z deltaAngleZ:(float)dz angleX:(float)x deltaAngleX:(float)dx; +/** initializes a CCOrbitCamera action with radius, delta-radius, z, deltaZ, x, deltaX */ +-(id) initWithDuration:(float) t radius:(float)r deltaRadius:(float) dr angleZ:(float)z deltaAngleZ:(float)dz angleX:(float)x deltaAngleX:(float)dx; +/** positions the camera according to spherical coordinates */ +-(void) sphericalRadius:(float*) r zenith:(float*) zenith azimuth:(float*) azimuth; +@end diff --git a/libs/cocos2d/CCActionCamera.m b/libs/cocos2d/CCActionCamera.m new file mode 100644 index 0000000..5134c6f --- /dev/null +++ b/libs/cocos2d/CCActionCamera.m @@ -0,0 +1,146 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CCActionCamera.h" +#import "CCNode.h" +#import "CCCamera.h" +#import "ccMacros.h" + +// +// CameraAction +// +@implementation CCActionCamera +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + CCCamera *camera = [target_ camera]; + [camera centerX:¢erXOrig_ centerY:¢erYOrig_ centerZ:¢erZOrig_]; + [camera eyeX:&eyeXOrig_ eyeY:&eyeYOrig_ eyeZ:&eyeZOrig_]; + [camera upX:&upXOrig_ upY:&upYOrig_ upZ: &upZOrig_]; +} + +-(id) reverse +{ + return [CCReverseTime actionWithAction:self]; +} +@end + +@implementation CCOrbitCamera ++(id) actionWithDuration:(float)t radius:(float)r deltaRadius:(float) dr angleZ:(float)z deltaAngleZ:(float)dz angleX:(float)x deltaAngleX:(float)dx +{ + return [[[self alloc] initWithDuration:t radius:r deltaRadius:dr angleZ:z deltaAngleZ:dz angleX:x deltaAngleX:dx] autorelease]; +} + +-(id) copyWithZone: (NSZone*) zone +{ + return [[[self class] allocWithZone: zone] initWithDuration:duration_ radius:radius_ deltaRadius:deltaRadius_ angleZ:angleZ_ deltaAngleZ:deltaAngleZ_ angleX:angleX_ deltaAngleX:deltaAngleX_]; +} + + +-(id) initWithDuration:(float)t radius:(float)r deltaRadius:(float) dr angleZ:(float)z deltaAngleZ:(float)dz angleX:(float)x deltaAngleX:(float)dx +{ + if((self=[super initWithDuration:t]) ) { + + radius_ = r; + deltaRadius_ = dr; + angleZ_ = z; + deltaAngleZ_ = dz; + angleX_ = x; + deltaAngleX_ = dx; + + radDeltaZ_ = (CGFloat)CC_DEGREES_TO_RADIANS(dz); + radDeltaX_ = (CGFloat)CC_DEGREES_TO_RADIANS(dx); + } + + return self; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + float r, zenith, azimuth; + + [self sphericalRadius: &r zenith:&zenith azimuth:&azimuth]; + +#if 0 // isnan() is not supported on the simulator, and isnan() always returns false. + if( isnan(radius_) ) + radius_ = r; + + if( isnan( angleZ_) ) + angleZ_ = (CGFloat)CC_RADIANS_TO_DEGREES(zenith); + + if( isnan( angleX_ ) ) + angleX_ = (CGFloat)CC_RADIANS_TO_DEGREES(azimuth); +#endif + + radZ_ = (CGFloat)CC_DEGREES_TO_RADIANS(angleZ_); + radX_ = (CGFloat)CC_DEGREES_TO_RADIANS(angleX_); +} + +-(void) update: (ccTime) dt +{ + float r = (radius_ + deltaRadius_ * dt) *[CCCamera getZEye]; + float za = radZ_ + radDeltaZ_ * dt; + float xa = radX_ + radDeltaX_ * dt; + + float i = sinf(za) * cosf(xa) * r + centerXOrig_; + float j = sinf(za) * sinf(xa) * r + centerYOrig_; + float k = cosf(za) * r + centerZOrig_; + + [[target_ camera] setEyeX:i eyeY:j eyeZ:k]; +} + +-(void) sphericalRadius:(float*) newRadius zenith:(float*) zenith azimuth:(float*) azimuth +{ + float ex, ey, ez, cx, cy, cz, x, y, z; + float r; // radius + float s; + + CCCamera *camera = [target_ camera]; + [camera eyeX:&ex eyeY:&ey eyeZ:&ez]; + [camera centerX:&cx centerY:&cy centerZ:&cz]; + + x = ex-cx; + y = ey-cy; + z = ez-cz; + + r = sqrtf( x*x + y*y + z*z); + s = sqrtf( x*x + y*y); + if(s==0.0f) + s = FLT_EPSILON; + if(r==0.0f) + r = FLT_EPSILON; + + *zenith = acosf( z/r); + if( x < 0 ) + *azimuth = (float)M_PI - asinf(y/s); + else + *azimuth = asinf(y/s); + + *newRadius = r / [CCCamera getZEye]; +} +@end diff --git a/libs/cocos2d/CCActionEase.h b/libs/cocos2d/CCActionEase.h new file mode 100644 index 0000000..fced701 --- /dev/null +++ b/libs/cocos2d/CCActionEase.h @@ -0,0 +1,159 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2009 Jason Booth + * + * 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 "CCActionInterval.h" + +/** Base class for Easing actions + */ +@interface CCActionEase : CCActionInterval +{ + CCActionInterval * other; +} +/** creates the action */ ++(id) actionWithAction: (CCActionInterval*) action; +/** initializes the action */ +-(id) initWithAction: (CCActionInterval*) action; +@end + +/** Base class for Easing actions with rate parameters + */ +@interface CCEaseRateAction : CCActionEase +{ + float rate; +} +/** rate value for the actions */ +@property (nonatomic,readwrite,assign) float rate; +/** Creates the action with the inner action and the rate parameter */ ++(id) actionWithAction: (CCActionInterval*) action rate:(float)rate; +/** Initializes the action with the inner action and the rate parameter */ +-(id) initWithAction: (CCActionInterval*) action rate:(float)rate; +@end + +/** CCEaseIn action with a rate + */ +@interface CCEaseIn : CCEaseRateAction {} @end + +/** CCEaseOut action with a rate + */ +@interface CCEaseOut : CCEaseRateAction {} @end + +/** CCEaseInOut action with a rate + */ +@interface CCEaseInOut : CCEaseRateAction {} @end + +/** CCEase Exponential In + */ +@interface CCEaseExponentialIn : CCActionEase {} @end +/** Ease Exponential Out + */ +@interface CCEaseExponentialOut : CCActionEase {} @end +/** Ease Exponential InOut + */ +@interface CCEaseExponentialInOut : CCActionEase {} @end +/** Ease Sine In + */ +@interface CCEaseSineIn : CCActionEase {} @end +/** Ease Sine Out + */ +@interface CCEaseSineOut : CCActionEase {} @end +/** Ease Sine InOut + */ +@interface CCEaseSineInOut : CCActionEase {} @end + +/** Ease Elastic abstract class + @since v0.8.2 + */ +@interface CCEaseElastic : CCActionEase +{ + float period_; +} + +/** period of the wave in radians. default is 0.3 */ +@property (nonatomic,readwrite) float period; + +/** Creates the action with the inner action and the period in radians (default is 0.3) */ ++(id) actionWithAction: (CCActionInterval*) action period:(float)period; +/** Initializes the action with the inner action and the period in radians (default is 0.3) */ +-(id) initWithAction: (CCActionInterval*) action period:(float)period; +@end + +/** Ease Elastic In action. + @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action. + @since v0.8.2 + */ +@interface CCEaseElasticIn : CCEaseElastic {} @end +/** Ease Elastic Out action. + @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action. + @since v0.8.2 + */ +@interface CCEaseElasticOut : CCEaseElastic {} @end +/** Ease Elastic InOut action. + @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action. + @since v0.8.2 + */ +@interface CCEaseElasticInOut : CCEaseElastic {} @end + +/** CCEaseBounce abstract class. + @since v0.8.2 +*/ +@interface CCEaseBounce : CCActionEase {} @end + +/** CCEaseBounceIn action. + @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action. + @since v0.8.2 +*/ +@interface CCEaseBounceIn : CCEaseBounce {} @end + +/** EaseBounceOut action. + @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action. + @since v0.8.2 + */ +@interface CCEaseBounceOut : CCEaseBounce {} @end + +/** CCEaseBounceInOut action. + @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action. + @since v0.8.2 + */ +@interface CCEaseBounceInOut : CCEaseBounce {} @end + +/** CCEaseBackIn action. + @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action. + @since v0.8.2 + */ +@interface CCEaseBackIn : CCActionEase {} @end + +/** CCEaseBackOut action. + @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action. + @since v0.8.2 + */ +@interface CCEaseBackOut : CCActionEase {} @end + +/** CCEaseBackInOut action. + @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action. + @since v0.8.2 + */ +@interface CCEaseBackInOut : CCActionEase {} @end + diff --git a/libs/cocos2d/CCActionEase.m b/libs/cocos2d/CCActionEase.m new file mode 100644 index 0000000..f28be11 --- /dev/null +++ b/libs/cocos2d/CCActionEase.m @@ -0,0 +1,534 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2009 Jason Booth + * + * 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. + * + */ + + +/* + * Elastic, Back and Bounce actions based on code from: + * http://github.com/NikhilK/silverlightfx/ + * + * by http://github.com/NikhilK + */ + +#import "CCActionEase.h" + +#ifndef M_PI_X_2 +#define M_PI_X_2 (float)M_PI * 2.0f +#endif + +#pragma mark EaseAction + +// +// EaseAction +// +@implementation CCActionEase + ++(id) actionWithAction: (CCActionInterval*) action +{ + return [[[self alloc] initWithAction: action] autorelease ]; +} + +-(id) initWithAction: (CCActionInterval*) action +{ + NSAssert( action!=nil, @"Ease: arguments must be non-nil"); + + if( (self=[super initWithDuration: action.duration]) ) + other = [action retain]; + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone:zone] initWithAction:[[other copy] autorelease]]; + return copy; +} + +-(void) dealloc +{ + [other release]; + [super dealloc]; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + [other startWithTarget:target_]; +} + +-(void) stop +{ + [other stop]; + [super stop]; +} + +-(void) update: (ccTime) t +{ + [other update: t]; +} + +-(CCActionInterval*) reverse +{ + return [[self class] actionWithAction: [other reverse]]; +} +@end + + +#pragma mark - +#pragma mark EaseRate + +// +// EaseRateAction +// +@implementation CCEaseRateAction +@synthesize rate; ++(id) actionWithAction: (CCActionInterval*) action rate:(float)aRate +{ + return [[[self alloc] initWithAction: action rate:aRate] autorelease ]; +} + +-(id) initWithAction: (CCActionInterval*) action rate:(float)aRate +{ + if( (self=[super initWithAction:action ]) ) + self.rate = aRate; + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone:zone] initWithAction:[[other copy] autorelease] rate:rate]; + return copy; +} + +-(void) dealloc +{ + [super dealloc]; +} + +-(CCActionInterval*) reverse +{ + return [[self class] actionWithAction: [other reverse] rate:1/rate]; +} +@end + +// +// EeseIn +// +@implementation CCEaseIn +-(void) update: (ccTime) t +{ + [other update: powf(t,rate)]; +} +@end + +// +// EaseOut +// +@implementation CCEaseOut +-(void) update: (ccTime) t +{ + [other update: powf(t,1/rate)]; +} +@end + +// +// EaseInOut +// +@implementation CCEaseInOut +-(void) update: (ccTime) t +{ + int sign =1; + int r = (int) rate; + if (r % 2 == 0) + sign = -1; + t *= 2; + if (t < 1) + [other update: 0.5f * powf (t, rate)]; + else + [other update: sign*0.5f * (powf (t-2, rate) + sign*2)]; +} + +// InOut and OutIn are symmetrical +-(CCActionInterval*) reverse +{ + return [[self class] actionWithAction: [other reverse] rate:rate]; +} + +@end + +#pragma mark - +#pragma mark EaseExponential + +// +// EaseExponentialIn +// +@implementation CCEaseExponentialIn +-(void) update: (ccTime) t +{ + [other update: (t==0) ? 0 : powf(2, 10 * (t/1 - 1)) - 1 * 0.001f]; +} + +- (CCActionInterval*) reverse +{ + return [CCEaseExponentialOut actionWithAction: [other reverse]]; +} +@end + +// +// EaseExponentialOut +// +@implementation CCEaseExponentialOut +-(void) update: (ccTime) t +{ + [other update: (t==1) ? 1 : (-powf(2, -10 * t/1) + 1)]; +} + +- (CCActionInterval*) reverse +{ + return [CCEaseExponentialIn actionWithAction: [other reverse]]; +} +@end + +// +// EaseExponentialInOut +// +@implementation CCEaseExponentialInOut +-(void) update: (ccTime) t +{ + t /= 0.5f; + if (t < 1) + t = 0.5f * powf(2, 10 * (t - 1)); + else + t = 0.5f * (-powf(2, -10 * (t -1) ) + 2); + + [other update:t]; +} +@end + + +#pragma mark - +#pragma mark EaseSin actions + +// +// EaseSineIn +// +@implementation CCEaseSineIn +-(void) update: (ccTime) t +{ + [other update:-1*cosf(t * (float)M_PI_2) +1]; +} + +- (CCActionInterval*) reverse +{ + return [CCEaseSineOut actionWithAction: [other reverse]]; +} +@end + +// +// EaseSineOut +// +@implementation CCEaseSineOut +-(void) update: (ccTime) t +{ + [other update:sinf(t * (float)M_PI_2)]; +} + +- (CCActionInterval*) reverse +{ + return [CCEaseSineIn actionWithAction: [other reverse]]; +} +@end + +// +// EaseSineInOut +// +@implementation CCEaseSineInOut +-(void) update: (ccTime) t +{ + [other update:-0.5f*(cosf( (float)M_PI*t) - 1)]; +} +@end + +#pragma mark - +#pragma mark EaseElastic actions + +// +// EaseElastic +// +@implementation CCEaseElastic + +@synthesize period = period_; + ++(id) actionWithAction: (CCActionInterval*) action +{ + return [[[self alloc] initWithAction:action period:0.3f] autorelease]; +} + ++(id) actionWithAction: (CCActionInterval*) action period:(float)period +{ + return [[[self alloc] initWithAction:action period:period] autorelease]; +} + +-(id) initWithAction: (CCActionInterval*) action +{ + return [self initWithAction:action period:0.3f]; +} + +-(id) initWithAction: (CCActionInterval*) action period:(float)period +{ + if( (self=[super initWithAction:action]) ) + period_ = period; + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone:zone] initWithAction:[[other copy] autorelease] period:period_]; + return copy; +} + +-(CCActionInterval*) reverse +{ + NSAssert(NO,@"Override me"); + return nil; +} + +@end + +// +// EaseElasticIn +// + +@implementation CCEaseElasticIn +-(void) update: (ccTime) t +{ + ccTime newT = 0; + if (t == 0 || t == 1) + newT = t; + + else { + float s = period_ / 4; + t = t - 1; + newT = -powf(2, 10 * t) * sinf( (t-s) *M_PI_X_2 / period_); + } + [other update:newT]; +} + +- (CCActionInterval*) reverse +{ + return [CCEaseElasticOut actionWithAction: [other reverse] period:period_]; +} + +@end + +// +// EaseElasticOut +// +@implementation CCEaseElasticOut + +-(void) update: (ccTime) t +{ + ccTime newT = 0; + if (t == 0 || t == 1) { + newT = t; + + } else { + float s = period_ / 4; + newT = powf(2, -10 * t) * sinf( (t-s) *M_PI_X_2 / period_) + 1; + } + [other update:newT]; +} + +- (CCActionInterval*) reverse +{ + return [CCEaseElasticIn actionWithAction: [other reverse] period:period_]; +} + +@end + +// +// EaseElasticInOut +// +@implementation CCEaseElasticInOut +-(void) update: (ccTime) t +{ + ccTime newT = 0; + + if( t == 0 || t == 1 ) + newT = t; + else { + t = t * 2; + if(! period_ ) + period_ = 0.3f * 1.5f; + ccTime s = period_ / 4; + + t = t -1; + if( t < 0 ) + newT = -0.5f * powf(2, 10 * t) * sinf((t - s) * M_PI_X_2 / period_); + else + newT = powf(2, -10 * t) * sinf((t - s) * M_PI_X_2 / period_) * 0.5f + 1; + } + [other update:newT]; +} + +- (CCActionInterval*) reverse +{ + return [CCEaseElasticInOut actionWithAction: [other reverse] period:period_]; +} + +@end + +#pragma mark - +#pragma mark EaseBounce actions + +// +// EaseBounce +// +@implementation CCEaseBounce +-(ccTime) bounceTime:(ccTime) t +{ + if (t < 1 / 2.75) { + return 7.5625f * t * t; + } + else if (t < 2 / 2.75) { + t -= 1.5f / 2.75f; + return 7.5625f * t * t + 0.75f; + } + else if (t < 2.5 / 2.75) { + t -= 2.25f / 2.75f; + return 7.5625f * t * t + 0.9375f; + } + + t -= 2.625f / 2.75f; + return 7.5625f * t * t + 0.984375f; +} +@end + +// +// EaseBounceIn +// + +@implementation CCEaseBounceIn + +-(void) update: (ccTime) t +{ + ccTime newT = 1 - [self bounceTime:1-t]; + [other update:newT]; +} + +- (CCActionInterval*) reverse +{ + return [CCEaseBounceOut actionWithAction: [other reverse]]; +} + +@end + +@implementation CCEaseBounceOut + +-(void) update: (ccTime) t +{ + ccTime newT = [self bounceTime:t]; + [other update:newT]; +} + +- (CCActionInterval*) reverse +{ + return [CCEaseBounceIn actionWithAction: [other reverse]]; +} + +@end + +@implementation CCEaseBounceInOut + +-(void) update: (ccTime) t +{ + ccTime newT = 0; + if (t < 0.5) { + t = t * 2; + newT = (1 - [self bounceTime:1-t] ) * 0.5f; + } else + newT = [self bounceTime:t * 2 - 1] * 0.5f + 0.5f; + + [other update:newT]; +} +@end + +#pragma mark - +#pragma mark Ease Back actions + +// +// EaseBackIn +// +@implementation CCEaseBackIn + +-(void) update: (ccTime) t +{ + ccTime overshoot = 1.70158f; + [other update: t * t * ((overshoot + 1) * t - overshoot)]; +} + +- (CCActionInterval*) reverse +{ + return [CCEaseBackOut actionWithAction: [other reverse]]; +} +@end + +// +// EaseBackOut +// +@implementation CCEaseBackOut +-(void) update: (ccTime) t +{ + ccTime overshoot = 1.70158f; + + t = t - 1; + [other update: t * t * ((overshoot + 1) * t + overshoot) + 1]; +} + +- (CCActionInterval*) reverse +{ + return [CCEaseBackIn actionWithAction: [other reverse]]; +} +@end + +// +// EaseBackInOut +// +@implementation CCEaseBackInOut + +-(void) update: (ccTime) t +{ + ccTime overshoot = 1.70158f * 1.525f; + + t = t * 2; + if (t < 1) + [other update: (t * t * ((overshoot + 1) * t - overshoot)) / 2]; + else { + t = t - 2; + [other update: (t * t * ((overshoot + 1) * t + overshoot)) / 2 + 1]; + } +} +@end diff --git a/libs/cocos2d/CCActionGrid.h b/libs/cocos2d/CCActionGrid.h new file mode 100644 index 0000000..13b6bc7 --- /dev/null +++ b/libs/cocos2d/CCActionGrid.h @@ -0,0 +1,165 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 On-Core + * + * 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 "CCActionInterval.h" +#import "CCActionInstant.h" +#import "CCGrid.h" + +@class CCGridBase; + +/** Base class for Grid actions */ +@interface CCGridAction : CCActionInterval +{ + ccGridSize gridSize_; +} + +/** size of the grid */ +@property (nonatomic,readwrite) ccGridSize gridSize; + +/** creates the action with size and duration */ ++(id) actionWithSize:(ccGridSize)size duration:(ccTime)d; +/** initializes the action with size and duration */ +-(id) initWithSize:(ccGridSize)gridSize duration:(ccTime)d; +/** returns the grid */ +-(CCGridBase *)grid; + +@end + +//////////////////////////////////////////////////////////// + +/** Base class for CCGrid3D actions. + Grid3D actions can modify a non-tiled grid. + */ +@interface CCGrid3DAction : CCGridAction +{ +} + +/** returns the vertex than belongs to certain position in the grid */ +-(ccVertex3F)vertex:(ccGridSize)pos; +/** returns the non-transformed vertex than belongs to certain position in the grid */ +-(ccVertex3F)originalVertex:(ccGridSize)pos; +/** sets a new vertex to a certain position of the grid */ +-(void)setVertex:(ccGridSize)pos vertex:(ccVertex3F)vertex; + +@end + +//////////////////////////////////////////////////////////// + +/** Base class for CCTiledGrid3D actions */ +@interface CCTiledGrid3DAction : CCGridAction +{ +} + +/** returns the tile that belongs to a certain position of the grid */ +-(ccQuad3)tile:(ccGridSize)pos; +/** returns the non-transformed tile that belongs to a certain position of the grid */ +-(ccQuad3)originalTile:(ccGridSize)pos; +/** sets a new tile to a certain position of the grid */ +-(void)setTile:(ccGridSize)pos coords:(ccQuad3)coords; + +@end + +//////////////////////////////////////////////////////////// + +/** CCAccelDeccelAmplitude action */ +@interface CCAccelDeccelAmplitude : CCActionInterval +{ + float rate; + CCActionInterval *other; +} + +/** amplitude rate */ +@property (nonatomic,readwrite) float rate; + +/** creates the action with an inner action that has the amplitude property, and a duration time */ ++(id)actionWithAction:(CCAction*)action duration:(ccTime)d; +/** initializes the action with an inner action that has the amplitude property, and a duration time */ +-(id)initWithAction:(CCAction*)action duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** CCAccelAmplitude action */ +@interface CCAccelAmplitude : CCActionInterval +{ + float rate; + CCActionInterval *other; +} + +/** amplitude rate */ +@property (nonatomic,readwrite) float rate; + +/** creates the action with an inner action that has the amplitude property, and a duration time */ ++(id)actionWithAction:(CCAction*)action duration:(ccTime)d; +/** initializes the action with an inner action that has the amplitude property, and a duration time */ +-(id)initWithAction:(CCAction*)action duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** CCDeccelAmplitude action */ +@interface CCDeccelAmplitude : CCActionInterval +{ + float rate; + CCActionInterval *other; +} + +/** amplitude rate */ +@property (nonatomic,readwrite) float rate; + +/** creates the action with an inner action that has the amplitude property, and a duration time */ ++(id)actionWithAction:(CCAction*)action duration:(ccTime)d; +/** initializes the action with an inner action that has the amplitude property, and a duration time */ +-(id)initWithAction:(CCAction*)action duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** CCStopGrid action. + Don't call this action if another grid action is active. + Call if you want to remove the the grid effect. Example: + [Sequence actions:[Lens ...], [StopGrid action], nil]; + */ +@interface CCStopGrid : CCActionInstant +{ +} +@end + +//////////////////////////////////////////////////////////// + +/** CCReuseGrid action */ +@interface CCReuseGrid : CCActionInstant +{ + int t; +} +/** creates an action with the number of times that the current grid will be reused */ ++(id) actionWithTimes: (int) times; +/** initializes an action with the number of times that the current grid will be reused */ +-(id) initWithTimes: (int) times; +@end diff --git a/libs/cocos2d/CCActionGrid.m b/libs/cocos2d/CCActionGrid.m new file mode 100644 index 0000000..b2d8f98 --- /dev/null +++ b/libs/cocos2d/CCActionGrid.m @@ -0,0 +1,386 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 On-Core + * + * 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 "CCActionGrid.h" +#import "CCDirector.h" + +#pragma mark - +#pragma mark GridAction + +@implementation CCGridAction + +@synthesize gridSize = gridSize_; + ++(id) actionWithSize:(ccGridSize)size duration:(ccTime)d +{ + return [[[self alloc] initWithSize:size duration:d ] autorelease]; +} + +-(id) initWithSize:(ccGridSize)gSize duration:(ccTime)d +{ + if ( (self = [super initWithDuration:d]) ) + { + gridSize_ = gSize; + } + + return self; +} + +-(void)startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + + CCGridBase *newgrid = [self grid]; + + CCNode *t = (CCNode*) target_; + CCGridBase *targetGrid = [t grid]; + + if ( targetGrid && targetGrid.reuseGrid > 0 ) + { + if ( targetGrid.active && targetGrid.gridSize.x == gridSize_.x && targetGrid.gridSize.y == gridSize_.y && [targetGrid isKindOfClass:[newgrid class]] ) + [targetGrid reuse]; + else + [NSException raise:@"GridBase" format:@"Cannot reuse grid"]; + } + else + { + if ( targetGrid && targetGrid.active ) + targetGrid.active = NO; + + t.grid = newgrid; + t.grid.active = YES; + } +} + +-(CCGridBase *)grid +{ + [NSException raise:@"GridBase" format:@"Abstract class needs implementation"]; + return nil; +} + +- (CCActionInterval*) reverse +{ + return [CCReverseTime actionWithAction:self]; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithSize:gridSize_ duration:duration_]; + return copy; +} +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark Grid3DAction + +@implementation CCGrid3DAction + +-(CCGridBase *)grid +{ + return [CCGrid3D gridWithSize:gridSize_]; +} + +-(ccVertex3F)vertex:(ccGridSize)pos +{ + CCGrid3D *g = (CCGrid3D *)[target_ grid]; + return [g vertex:pos]; +} + +-(ccVertex3F)originalVertex:(ccGridSize)pos +{ + CCGrid3D *g = (CCGrid3D *)[target_ grid]; + return [g originalVertex:pos]; +} + +-(void)setVertex:(ccGridSize)pos vertex:(ccVertex3F)vertex +{ + CCGrid3D *g = (CCGrid3D *)[target_ grid]; + return [g setVertex:pos vertex:vertex]; +} +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark TiledGrid3DAction + +@implementation CCTiledGrid3DAction + +-(CCGridBase *)grid +{ + return [CCTiledGrid3D gridWithSize:gridSize_]; +} + +-(ccQuad3)tile:(ccGridSize)pos +{ + CCTiledGrid3D *g = (CCTiledGrid3D *)[target_ grid]; + return [g tile:pos]; +} + +-(ccQuad3)originalTile:(ccGridSize)pos +{ + CCTiledGrid3D *g = (CCTiledGrid3D *)[target_ grid]; + return [g originalTile:pos]; +} + +-(void)setTile:(ccGridSize)pos coords:(ccQuad3)coords +{ + CCTiledGrid3D *g = (CCTiledGrid3D *)[target_ grid]; + [g setTile:pos coords:coords]; +} + +@end + +//////////////////////////////////////////////////////////// + +@interface CCActionInterval (Amplitude) +-(void)setAmplitudeRate:(CGFloat)amp; +-(CGFloat)getAmplitudeRate; +@end + +@implementation CCActionInterval (Amplitude) +-(void)setAmplitudeRate:(CGFloat)amp +{ + [NSException raise:@"IntervalAction (Amplitude)" format:@"Abstract class needs implementation"]; +} + +-(CGFloat)getAmplitudeRate +{ + [NSException raise:@"IntervalAction (Amplitude)" format:@"Abstract class needs implementation"]; + return 0; +} +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark AccelDeccelAmplitude + +@implementation CCAccelDeccelAmplitude + +@synthesize rate; + ++(id)actionWithAction:(CCAction*)action duration:(ccTime)d +{ + return [[[self alloc] initWithAction:action duration:d ] autorelease]; +} + +-(id)initWithAction:(CCAction *)action duration:(ccTime)d +{ + if ( (self = [super initWithDuration:d]) ) + { + rate = 1.0f; + other = [action retain]; + } + + return self; +} + +-(void)dealloc +{ + [other release]; + [super dealloc]; +} + +-(void)startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + [other startWithTarget:target_]; +} + +-(void) update: (ccTime) time +{ + float f = time*2; + + if (f > 1) + { + f -= 1; + f = 1 - f; + } + + [other setAmplitudeRate:powf(f, rate)]; + [other update:time]; +} + +- (CCActionInterval*) reverse +{ + return [CCAccelDeccelAmplitude actionWithAction:[other reverse] duration:duration_]; +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark AccelAmplitude + +@implementation CCAccelAmplitude + +@synthesize rate; + ++(id)actionWithAction:(CCAction*)action duration:(ccTime)d +{ + return [[[self alloc] initWithAction:action duration:d ] autorelease]; +} + +-(id)initWithAction:(CCAction *)action duration:(ccTime)d +{ + if ( (self = [super initWithDuration:d]) ) + { + rate = 1.0f; + other = [action retain]; + } + + return self; +} + +-(void)dealloc +{ + [other release]; + [super dealloc]; +} + +-(void)startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + [other startWithTarget:target_]; +} + +-(void) update: (ccTime) time +{ + [other setAmplitudeRate:powf(time, rate)]; + [other update:time]; +} + +- (CCActionInterval*) reverse +{ + return [CCAccelAmplitude actionWithAction:[other reverse] duration:self.duration]; +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark DeccelAmplitude + +@implementation CCDeccelAmplitude + +@synthesize rate; + ++(id)actionWithAction:(CCAction*)action duration:(ccTime)d +{ + return [[[self alloc] initWithAction:action duration:d ] autorelease]; +} + +-(id)initWithAction:(CCAction *)action duration:(ccTime)d +{ + if ( (self = [super initWithDuration:d]) ) + { + rate = 1.0f; + other = [action retain]; + } + + return self; +} + +-(void)dealloc +{ + [other release]; + [super dealloc]; +} + +-(void)startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + [other startWithTarget:target_]; +} + +-(void) update: (ccTime) time +{ + [other setAmplitudeRate:powf((1-time), rate)]; + [other update:time]; +} + +- (CCActionInterval*) reverse +{ + return [CCDeccelAmplitude actionWithAction:[other reverse] duration:self.duration]; +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark StopGrid + +@implementation CCStopGrid + +-(void)startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + + if ( [[self target] grid] && [[[self target] grid] active] ) { + [[[self target] grid] setActive: NO]; + +// [[self target] setGrid: nil]; + } +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark ReuseGrid + +@implementation CCReuseGrid + ++(id)actionWithTimes:(int)times +{ + return [[[self alloc] initWithTimes:times ] autorelease]; +} + +-(id)initWithTimes:(int)times +{ + if ( (self = [super init]) ) + t = times; + + return self; +} + +-(void)startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + + CCNode *myTarget = (CCNode*) [self target]; + if ( myTarget.grid && myTarget.grid.active ) + myTarget.grid.reuseGrid += t; +} + +@end diff --git a/libs/cocos2d/CCActionGrid3D.h b/libs/cocos2d/CCActionGrid3D.h new file mode 100644 index 0000000..a8003f4 --- /dev/null +++ b/libs/cocos2d/CCActionGrid3D.h @@ -0,0 +1,208 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 On-Core + * + * 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 "CCActionGrid.h" + +/** CCWaves3D action */ +@interface CCWaves3D : CCGrid3DAction +{ + int waves; + float amplitude; + float amplitudeRate; +} + +/** amplitude of the wave */ +@property (nonatomic,readwrite) float amplitude; +/** amplitude rate of the wave */ +@property (nonatomic,readwrite) float amplitudeRate; + ++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d; +-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** CCFlipX3D action */ +@interface CCFlipX3D : CCGrid3DAction +{ +} + +/** creates the action with duration */ ++(id) actionWithDuration:(ccTime)d; +/** initizlies the action with duration */ +-(id) initWithDuration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** CCFlipY3D action */ +@interface CCFlipY3D : CCFlipX3D +{ +} + +@end + +//////////////////////////////////////////////////////////// + +/** CCLens3D action */ +@interface CCLens3D : CCGrid3DAction +{ + CGPoint position_; + CGPoint positionInPixels_; + float radius_; + float lensEffect_; + BOOL dirty_; +} + +/** lens effect. Defaults to 0.7 - 0 means no effect, 1 is very strong effect */ +@property (nonatomic,readwrite) float lensEffect; +/** lens center position in Points */ +@property (nonatomic,readwrite) CGPoint position; + +/** creates the action with center position in Points, radius, a grid size and duration */ ++(id)actionWithPosition:(CGPoint)pos radius:(float)r grid:(ccGridSize)gridSize duration:(ccTime)d; +/** initializes the action with center position in Points, radius, a grid size and duration */ +-(id)initWithPosition:(CGPoint)pos radius:(float)r grid:(ccGridSize)gridSize duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** CCRipple3D action */ +@interface CCRipple3D : CCGrid3DAction +{ + CGPoint position_; + CGPoint positionInPixels_; + float radius_; + int waves_; + float amplitude_; + float amplitudeRate_; +} + +/** center position in Points */ +@property (nonatomic,readwrite) CGPoint position; +/** amplitude */ +@property (nonatomic,readwrite) float amplitude; +/** amplitude rate */ +@property (nonatomic,readwrite) float amplitudeRate; + +/** creates the action with a position in points, radius, number of waves, amplitude, a grid size and duration */ ++(id)actionWithPosition:(CGPoint)pos radius:(float)r waves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d; +/** initializes the action with a position in points, radius, number of waves, amplitude, a grid size and duration */ +-(id)initWithPosition:(CGPoint)pos radius:(float)r waves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** CCShaky3D action */ +@interface CCShaky3D : CCGrid3DAction +{ + int randrange; + BOOL shakeZ; +} + +/** creates the action with a range, shake Z vertices, a grid and duration */ ++(id)actionWithRange:(int)range shakeZ:(BOOL)shakeZ grid:(ccGridSize)gridSize duration:(ccTime)d; +/** initializes the action with a range, shake Z vertices, a grid and duration */ +-(id)initWithRange:(int)range shakeZ:(BOOL)shakeZ grid:(ccGridSize)gridSize duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** CCLiquid action */ +@interface CCLiquid : CCGrid3DAction +{ + int waves; + float amplitude; + float amplitudeRate; + +} + +/** amplitude */ +@property (nonatomic,readwrite) float amplitude; +/** amplitude rate */ +@property (nonatomic,readwrite) float amplitudeRate; + +/** creates the action with amplitude, a grid and duration */ ++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d; +/** initializes the action with amplitude, a grid and duration */ +-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** CCWaves action */ +@interface CCWaves : CCGrid3DAction +{ + int waves; + float amplitude; + float amplitudeRate; + BOOL vertical; + BOOL horizontal; +} + +/** amplitude */ +@property (nonatomic,readwrite) float amplitude; +/** amplitude rate */ +@property (nonatomic,readwrite) float amplitudeRate; + +/** initializes the action with amplitude, horizontal sin, vertical sin, a grid and duration */ ++(id)actionWithWaves:(int)wav amplitude:(float)amp horizontal:(BOOL)h vertical:(BOOL)v grid:(ccGridSize)gridSize duration:(ccTime)d; +/** creates the action with amplitude, horizontal sin, vertical sin, a grid and duration */ +-(id)initWithWaves:(int)wav amplitude:(float)amp horizontal:(BOOL)h vertical:(BOOL)v grid:(ccGridSize)gridSize duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** CCTwirl action */ +@interface CCTwirl : CCGrid3DAction +{ + CGPoint position_; + CGPoint positionInPixels_; + int twirls_; + float amplitude_; + float amplitudeRate_; +} + +/** twirl center */ +@property (nonatomic,readwrite) CGPoint position; +/** amplitude */ +@property (nonatomic,readwrite) float amplitude; +/** amplitude rate */ +@property (nonatomic,readwrite) float amplitudeRate; + +/** creates the action with center position, number of twirls, amplitude, a grid size and duration */ ++(id)actionWithPosition:(CGPoint)pos twirls:(int)t amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d; +/** initializes the action with center position, number of twirls, amplitude, a grid size and duration */ +-(id)initWithPosition:(CGPoint)pos twirls:(int)t amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d; + +@end diff --git a/libs/cocos2d/CCActionGrid3D.m b/libs/cocos2d/CCActionGrid3D.m new file mode 100644 index 0000000..1d4a783 --- /dev/null +++ b/libs/cocos2d/CCActionGrid3D.m @@ -0,0 +1,659 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 On-Core + * + * 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 "CCActionGrid3D.h" +#import "ccMacros.h" +#import "Support/CGPointExtension.h" + +#pragma mark - +#pragma mark Waves3D + +@implementation CCWaves3D + +@synthesize amplitude; +@synthesize amplitudeRate; + ++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d +{ + return [[[self alloc] initWithWaves:wav amplitude:amp grid:gridSize duration:d] autorelease]; +} + +-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d +{ + if ( (self = [super initWithSize:gSize duration:d]) ) + { + waves = wav; + amplitude = amp; + amplitudeRate = 1.0f; + } + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithWaves:waves amplitude:amplitude grid:gridSize_ duration:duration_]; + return copy; +} + + +-(void)update:(ccTime)time +{ + int i, j; + + for( i = 0; i < (gridSize_.x+1); i++ ) + { + for( j = 0; j < (gridSize_.y+1); j++ ) + { + ccVertex3F v = [self originalVertex:ccg(i,j)]; + v.z += (sinf((CGFloat)M_PI*time*waves*2 + (v.y+v.x) * .01f) * amplitude * amplitudeRate); + [self setVertex:ccg(i,j) vertex:v]; + } + } +} +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark FlipX3D + +@implementation CCFlipX3D + ++(id) actionWithDuration:(ccTime)d +{ + return [[[self alloc] initWithSize:ccg(1,1) duration:d] autorelease]; +} + +-(id) initWithDuration:(ccTime)d +{ + return [super initWithSize:ccg(1,1) duration:d]; +} + +-(id)initWithSize:(ccGridSize)gSize duration:(ccTime)d +{ + if ( gSize.x != 1 || gSize.y != 1 ) + { + [NSException raise:@"FlipX3D" format:@"Grid size must be (1,1)"]; + } + + return [super initWithSize:gSize duration:d]; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithSize:gridSize_ duration:duration_]; + return copy; +} + + +-(void)update:(ccTime)time +{ + CGFloat angle = (CGFloat)M_PI * time; // 180 degrees + CGFloat mz = sinf( angle ); + angle = angle / 2.0f; // x calculates degrees from 0 to 90 + CGFloat mx = cosf( angle ); + + ccVertex3F v0, v1, v, diff; + + v0 = [self originalVertex:ccg(1,1)]; + v1 = [self originalVertex:ccg(0,0)]; + + CGFloat x0 = v0.x; + CGFloat x1 = v1.x; + CGFloat x; + ccGridSize a, b, c, d; + + if ( x0 > x1 ) + { + // Normal Grid + a = ccg(0,0); + b = ccg(0,1); + c = ccg(1,0); + d = ccg(1,1); + x = x0; + } + else + { + // Reversed Grid + c = ccg(0,0); + d = ccg(0,1); + a = ccg(1,0); + b = ccg(1,1); + x = x1; + } + + diff.x = ( x - x * mx ); + diff.z = fabsf( floorf( (x * mz) / 4.0f ) ); + +// bottom-left + v = [self originalVertex:a]; + v.x = diff.x; + v.z += diff.z; + [self setVertex:a vertex:v]; + +// upper-left + v = [self originalVertex:b]; + v.x = diff.x; + v.z += diff.z; + [self setVertex:b vertex:v]; + +// bottom-right + v = [self originalVertex:c]; + v.x -= diff.x; + v.z -= diff.z; + [self setVertex:c vertex:v]; + +// upper-right + v = [self originalVertex:d]; + v.x -= diff.x; + v.z -= diff.z; + [self setVertex:d vertex:v]; +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark FlipY3D + +@implementation CCFlipY3D + +-(void)update:(ccTime)time +{ + CGFloat angle = (CGFloat)M_PI * time; // 180 degrees + CGFloat mz = sinf( angle ); + angle = angle / 2.0f; // x calculates degrees from 0 to 90 + CGFloat my = cosf( angle ); + + ccVertex3F v0, v1, v, diff; + + v0 = [self originalVertex:ccg(1,1)]; + v1 = [self originalVertex:ccg(0,0)]; + + CGFloat y0 = v0.y; + CGFloat y1 = v1.y; + CGFloat y; + ccGridSize a, b, c, d; + + if ( y0 > y1 ) + { + // Normal Grid + a = ccg(0,0); + b = ccg(0,1); + c = ccg(1,0); + d = ccg(1,1); + y = y0; + } + else + { + // Reversed Grid + b = ccg(0,0); + a = ccg(0,1); + d = ccg(1,0); + c = ccg(1,1); + y = y1; + } + + diff.y = y - y * my; + diff.z = fabsf( floorf( (y * mz) / 4.0f ) ); + + // bottom-left + v = [self originalVertex:a]; + v.y = diff.y; + v.z += diff.z; + [self setVertex:a vertex:v]; + + // upper-left + v = [self originalVertex:b]; + v.y -= diff.y; + v.z -= diff.z; + [self setVertex:b vertex:v]; + + // bottom-right + v = [self originalVertex:c]; + v.y = diff.y; + v.z += diff.z; + [self setVertex:c vertex:v]; + + // upper-right + v = [self originalVertex:d]; + v.y -= diff.y; + v.z -= diff.z; + [self setVertex:d vertex:v]; +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark Lens3D + +@implementation CCLens3D + +@synthesize lensEffect=lensEffect_; + ++(id)actionWithPosition:(CGPoint)pos radius:(float)r grid:(ccGridSize)gridSize duration:(ccTime)d +{ + return [[[self alloc] initWithPosition:pos radius:r grid:gridSize duration:d] autorelease]; +} + +-(id)initWithPosition:(CGPoint)pos radius:(float)r grid:(ccGridSize)gSize duration:(ccTime)d +{ + if ( (self = [super initWithSize:gSize duration:d]) ) + { + position_ = ccp(-1,-1); + self.position = pos; + radius_ = r; + lensEffect_ = 0.7f; + dirty_ = YES; + } + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithPosition:position_ radius:radius_ grid:gridSize_ duration:duration_]; + return copy; +} + +-(void) setPosition:(CGPoint)pos +{ + if( ! CGPointEqualToPoint(pos, position_) ) { + position_ = pos; + positionInPixels_.x = pos.x * CC_CONTENT_SCALE_FACTOR(); + positionInPixels_.y = pos.y * CC_CONTENT_SCALE_FACTOR(); + + dirty_ = YES; + } +} + +-(CGPoint) position +{ + return position_; +} + +-(void)update:(ccTime)time +{ + if ( dirty_ ) + { + int i, j; + + for( i = 0; i < gridSize_.x+1; i++ ) + { + for( j = 0; j < gridSize_.y+1; j++ ) + { + ccVertex3F v = [self originalVertex:ccg(i,j)]; + CGPoint vect = ccpSub(positionInPixels_, ccp(v.x,v.y)); + CGFloat r = ccpLength(vect); + + if ( r < radius_ ) + { + r = radius_ - r; + CGFloat pre_log = r / radius_; + if ( pre_log == 0 ) pre_log = 0.001f; + float l = logf(pre_log) * lensEffect_; + float new_r = expf( l ) * radius_; + + if ( ccpLength(vect) > 0 ) + { + vect = ccpNormalize(vect); + CGPoint new_vect = ccpMult(vect, new_r); + v.z += ccpLength(new_vect) * lensEffect_; + } + } + + [self setVertex:ccg(i,j) vertex:v]; + } + } + + dirty_ = NO; + } +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark Ripple3D + +@implementation CCRipple3D + +@synthesize amplitude = amplitude_; +@synthesize amplitudeRate = amplitudeRate_; + ++(id)actionWithPosition:(CGPoint)pos radius:(float)r waves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d +{ + return [[[self alloc] initWithPosition:pos radius:r waves:wav amplitude:amp grid:gridSize duration:d] autorelease]; +} + +-(id)initWithPosition:(CGPoint)pos radius:(float)r waves:(int)wav amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d +{ + if ( (self = [super initWithSize:gSize duration:d]) ) + { + self.position = pos; + radius_ = r; + waves_ = wav; + amplitude_ = amp; + amplitudeRate_ = 1.0f; + } + + return self; +} + +-(CGPoint) position +{ + return position_; +} + +-(void) setPosition:(CGPoint)pos +{ + position_ = pos; + positionInPixels_.x = pos.x * CC_CONTENT_SCALE_FACTOR(); + positionInPixels_.y = pos.y * CC_CONTENT_SCALE_FACTOR(); +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithPosition:position_ radius:radius_ waves:waves_ amplitude:amplitude_ grid:gridSize_ duration:duration_]; + return copy; +} + + +-(void)update:(ccTime)time +{ + int i, j; + + for( i = 0; i < (gridSize_.x+1); i++ ) + { + for( j = 0; j < (gridSize_.y+1); j++ ) + { + ccVertex3F v = [self originalVertex:ccg(i,j)]; + CGPoint vect = ccpSub(positionInPixels_, ccp(v.x,v.y)); + CGFloat r = ccpLength(vect); + + if ( r < radius_ ) + { + r = radius_ - r; + CGFloat rate = powf( r / radius_, 2); + v.z += (sinf( time*(CGFloat)M_PI*waves_*2 + r * 0.1f) * amplitude_ * amplitudeRate_ * rate ); + } + + [self setVertex:ccg(i,j) vertex:v]; + } + } +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark Shaky3D + +@implementation CCShaky3D + ++(id)actionWithRange:(int)range shakeZ:(BOOL)sz grid:(ccGridSize)gridSize duration:(ccTime)d +{ + return [[[self alloc] initWithRange:range shakeZ:sz grid:gridSize duration:d] autorelease]; +} + +-(id)initWithRange:(int)range shakeZ:(BOOL)sz grid:(ccGridSize)gSize duration:(ccTime)d +{ + if ( (self = [super initWithSize:gSize duration:d]) ) + { + randrange = range; + shakeZ = sz; + } + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithRange:randrange shakeZ:shakeZ grid:gridSize_ duration:duration_]; + return copy; +} + +-(void)update:(ccTime)time +{ + int i, j; + + for( i = 0; i < (gridSize_.x+1); i++ ) + { + for( j = 0; j < (gridSize_.y+1); j++ ) + { + ccVertex3F v = [self originalVertex:ccg(i,j)]; + v.x += ( rand() % (randrange*2) ) - randrange; + v.y += ( rand() % (randrange*2) ) - randrange; + if( shakeZ ) + v.z += ( rand() % (randrange*2) ) - randrange; + + [self setVertex:ccg(i,j) vertex:v]; + } + } +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark Liquid + +@implementation CCLiquid + +@synthesize amplitude; +@synthesize amplitudeRate; + ++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d +{ + return [[[self alloc] initWithWaves:wav amplitude:amp grid:gridSize duration:d] autorelease]; +} + +-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d +{ + if ( (self = [super initWithSize:gSize duration:d]) ) + { + waves = wav; + amplitude = amp; + amplitudeRate = 1.0f; + } + + return self; +} + +-(void)update:(ccTime)time +{ + int i, j; + + for( i = 1; i < gridSize_.x; i++ ) + { + for( j = 1; j < gridSize_.y; j++ ) + { + ccVertex3F v = [self originalVertex:ccg(i,j)]; + v.x = (v.x + (sinf(time*(CGFloat)M_PI*waves*2 + v.x * .01f) * amplitude * amplitudeRate)); + v.y = (v.y + (sinf(time*(CGFloat)M_PI*waves*2 + v.y * .01f) * amplitude * amplitudeRate)); + [self setVertex:ccg(i,j) vertex:v]; + } + } +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithWaves:waves amplitude:amplitude grid:gridSize_ duration:duration_]; + return copy; +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark Waves + +@implementation CCWaves + +@synthesize amplitude; +@synthesize amplitudeRate; + ++(id)actionWithWaves:(int)wav amplitude:(float)amp horizontal:(BOOL)h vertical:(BOOL)v grid:(ccGridSize)gridSize duration:(ccTime)d +{ + return [[[self alloc] initWithWaves:wav amplitude:amp horizontal:h vertical:v grid:gridSize duration:d] autorelease]; +} + +-(id)initWithWaves:(int)wav amplitude:(float)amp horizontal:(BOOL)h vertical:(BOOL)v grid:(ccGridSize)gSize duration:(ccTime)d +{ + if ( (self = [super initWithSize:gSize duration:d]) ) + { + waves = wav; + amplitude = amp; + amplitudeRate = 1.0f; + horizontal = h; + vertical = v; + } + + return self; +} + +-(void)update:(ccTime)time +{ + int i, j; + + for( i = 0; i < (gridSize_.x+1); i++ ) + { + for( j = 0; j < (gridSize_.y+1); j++ ) + { + ccVertex3F v = [self originalVertex:ccg(i,j)]; + + if ( vertical ) + v.x = (v.x + (sinf(time*(CGFloat)M_PI*waves*2 + v.y * .01f) * amplitude * amplitudeRate)); + + if ( horizontal ) + v.y = (v.y + (sinf(time*(CGFloat)M_PI*waves*2 + v.x * .01f) * amplitude * amplitudeRate)); + + [self setVertex:ccg(i,j) vertex:v]; + } + } +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithWaves:waves amplitude:amplitude horizontal:horizontal vertical:vertical grid:gridSize_ duration:duration_]; + return copy; +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark Twirl + +@implementation CCTwirl + +@synthesize amplitude = amplitude_; +@synthesize amplitudeRate = amplitudeRate_; + ++(id)actionWithPosition:(CGPoint)pos twirls:(int)t amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d +{ + return [[[self alloc] initWithPosition:pos twirls:t amplitude:amp grid:gridSize duration:d] autorelease]; +} + +-(id)initWithPosition:(CGPoint)pos twirls:(int)t amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d +{ + if ( (self = [super initWithSize:gSize duration:d]) ) + { + self.position = pos; + twirls_ = t; + amplitude_ = amp; + amplitudeRate_ = 1.0f; + } + + return self; +} + +-(void) setPosition:(CGPoint)pos +{ + position_ = pos; + positionInPixels_.x = pos.x * CC_CONTENT_SCALE_FACTOR(); + positionInPixels_.y = pos.y * CC_CONTENT_SCALE_FACTOR(); +} + +-(CGPoint) position +{ + return position_; +} + +-(void)update:(ccTime)time +{ + int i, j; + CGPoint c = positionInPixels_; + + for( i = 0; i < (gridSize_.x+1); i++ ) + { + for( j = 0; j < (gridSize_.y+1); j++ ) + { + ccVertex3F v = [self originalVertex:ccg(i,j)]; + + CGPoint avg = ccp(i-(gridSize_.x/2.0f), j-(gridSize_.y/2.0f)); + CGFloat r = ccpLength( avg ); + + CGFloat amp = 0.1f * amplitude_ * amplitudeRate_; + CGFloat a = r * cosf( (CGFloat)M_PI/2.0f + time * (CGFloat)M_PI * twirls_ * 2 ) * amp; + + float cosA = cosf(a); + float sinA = sinf(a); + + CGPoint d = { + sinA * (v.y-c.y) + cosA * (v.x-c.x), + cosA * (v.y-c.y) - sinA * (v.x-c.x) + }; + + v.x = c.x + d.x; + v.y = c.y + d.y; + + [self setVertex:ccg(i,j) vertex:v]; + } + } +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithPosition:position_ + twirls:twirls_ + amplitude:amplitude_ + grid:gridSize_ + duration:duration_]; + return copy; +} + + +@end diff --git a/libs/cocos2d/CCActionInstant.h b/libs/cocos2d/CCActionInstant.h new file mode 100644 index 0000000..c94b0d4 --- /dev/null +++ b/libs/cocos2d/CCActionInstant.h @@ -0,0 +1,204 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CCAction.h" + +/** Instant actions are immediate actions. They don't have a duration like + the CCIntervalAction actions. +*/ +@interface CCActionInstant : CCFiniteTimeAction +{ +} +@end + +/** Show the node + */ + @interface CCShow : CCActionInstant +{ +} +@end + +/** Hide the node + */ +@interface CCHide : CCActionInstant +{ +} +@end + +/** Toggles the visibility of a node + */ +@interface CCToggleVisibility : CCActionInstant +{ +} +@end + +/** Flips the sprite horizontally + @since v0.99.0 + */ +@interface CCFlipX : CCActionInstant +{ + BOOL flipX; +} ++(id) actionWithFlipX:(BOOL)x; +-(id) initWithFlipX:(BOOL)x; +@end + +/** Flips the sprite vertically + @since v0.99.0 + */ +@interface CCFlipY : CCActionInstant +{ + BOOL flipY; +} ++(id) actionWithFlipY:(BOOL)y; +-(id) initWithFlipY:(BOOL)y; +@end + +/** Places the node in a certain position + */ +@interface CCPlace : CCActionInstant +{ + CGPoint position; +} +/** creates a Place action with a position */ ++(id) actionWithPosition: (CGPoint) pos; +/** Initializes a Place action with a position */ +-(id) initWithPosition: (CGPoint) pos; +@end + +/** Calls a 'callback' + */ +@interface CCCallFunc : CCActionInstant +{ + id targetCallback_; + SEL selector_; +} + +/** Target that will be called */ +@property (nonatomic, readwrite, retain) id targetCallback; + +/** creates the action with the callback */ ++(id) actionWithTarget: (id) t selector:(SEL) s; +/** initializes the action with the callback */ +-(id) initWithTarget: (id) t selector:(SEL) s; +/** exeuctes the callback */ +-(void) execute; +@end + +/** Calls a 'callback' with the node as the first argument. + N means Node + */ +@interface CCCallFuncN : CCCallFunc +{ +} +@end + +typedef void (*CC_CALLBACK_ND)(id, SEL, id, void *); +/** Calls a 'callback' with the node as the first argument and the 2nd argument is data. + * ND means: Node and Data. Data is void *, so it could be anything. + */ +@interface CCCallFuncND : CCCallFuncN +{ + void *data_; + CC_CALLBACK_ND callbackMethod_; +} + +/** Invocation object that has the target#selector and the parameters */ +@property (nonatomic,readwrite) CC_CALLBACK_ND callbackMethod; + +/** creates the action with the callback and the data to pass as an argument */ ++(id) actionWithTarget: (id) t selector:(SEL) s data:(void*)d; +/** initializes the action with the callback and the data to pass as an argument */ +-(id) initWithTarget:(id) t selector:(SEL) s data:(void*) d; +@end + +/** Calls a 'callback' with an object as the first argument. + O means Object. + @since v0.99.5 + */ +@interface CCCallFuncO : CCCallFunc +{ + id object_; +} +/** object to be passed as argument */ +@property (nonatomic, readwrite, retain) id object; + +/** creates the action with the callback and the object to pass as an argument */ ++(id) actionWithTarget: (id) t selector:(SEL) s object:(id)object; +/** initializes the action with the callback and the object to pass as an argument */ +-(id) initWithTarget:(id) t selector:(SEL) s object:(id)object; + +@end + +#pragma mark Blocks Support + +#if NS_BLOCKS_AVAILABLE + +/** Executes a callback using a block. + */ +@interface CCCallBlock : CCActionInstant +{ + void (^block_)(); +} + +/** creates the action with the specified block, to be used as a callback. + The block will be "copied". + */ ++(id) actionWithBlock:(void(^)())block; + +/** initialized the action with the specified block, to be used as a callback. + The block will be "copied". + */ +-(id) initWithBlock:(void(^)())block; + +/** executes the callback */ +-(void) execute; +@end + +@class CCNode; + +/** Executes a callback using a block with a single CCNode parameter. + */ +@interface CCCallBlockN : CCActionInstant +{ + void (^block_)(CCNode *); +} + +/** creates the action with the specified block, to be used as a callback. + The block will be "copied". + */ ++(id) actionWithBlock:(void(^)(CCNode *node))block; + +/** initialized the action with the specified block, to be used as a callback. + The block will be "copied". + */ +-(id) initWithBlock:(void(^)(CCNode *node))block; + +/** executes the callback */ +-(void) execute; +@end + +#endif diff --git a/libs/cocos2d/CCActionInstant.m b/libs/cocos2d/CCActionInstant.m new file mode 100644 index 0000000..cd4ac3d --- /dev/null +++ b/libs/cocos2d/CCActionInstant.m @@ -0,0 +1,476 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CCBlockSupport.h" +#import "CCActionInstant.h" +#import "CCNode.h" +#import "CCSprite.h" + + +// +// InstantAction +// +#pragma mark CCActionInstant + +@implementation CCActionInstant + +-(id) init +{ + if( (self=[super init]) ) + duration_ = 0; + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCActionInstant *copy = [[[self class] allocWithZone: zone] init]; + return copy; +} + +- (BOOL) isDone +{ + return YES; +} + +-(void) step: (ccTime) dt +{ + [self update: 1]; +} + +-(void) update: (ccTime) t +{ + // ignore +} + +-(CCFiniteTimeAction*) reverse +{ + return [[self copy] autorelease]; +} +@end + +// +// Show +// +#pragma mark CCShow + +@implementation CCShow +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + ((CCNode *)target_).visible = YES; +} + +-(CCFiniteTimeAction*) reverse +{ + return [CCHide action]; +} +@end + +// +// Hide +// +#pragma mark CCHide + +@implementation CCHide +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + ((CCNode *)target_).visible = NO; +} + +-(CCFiniteTimeAction*) reverse +{ + return [CCShow action]; +} +@end + +// +// ToggleVisibility +// +#pragma mark CCToggleVisibility + +@implementation CCToggleVisibility +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + ((CCNode *)target_).visible = !((CCNode *)target_).visible; +} +@end + +// +// FlipX +// +#pragma mark CCFlipX + +@implementation CCFlipX ++(id) actionWithFlipX:(BOOL)x +{ + return [[[self alloc] initWithFlipX:x] autorelease]; +} + +-(id) initWithFlipX:(BOOL)x +{ + if(( self=[super init])) + flipX = x; + + return self; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + [(CCSprite*)aTarget setFlipX:flipX]; +} + +-(CCFiniteTimeAction*) reverse +{ + return [CCFlipX actionWithFlipX:!flipX]; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithFlipX:flipX]; + return copy; +} +@end + +// +// FlipY +// +#pragma mark CCFlipY + +@implementation CCFlipY ++(id) actionWithFlipY:(BOOL)y +{ + return [[[self alloc] initWithFlipY:y] autorelease]; +} + +-(id) initWithFlipY:(BOOL)y +{ + if(( self=[super init])) + flipY = y; + + return self; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + [(CCSprite*)aTarget setFlipY:flipY]; +} + +-(CCFiniteTimeAction*) reverse +{ + return [CCFlipY actionWithFlipY:!flipY]; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithFlipY:flipY]; + return copy; +} +@end + + +// +// Place +// +#pragma mark CCPlace + +@implementation CCPlace ++(id) actionWithPosition: (CGPoint) pos +{ + return [[[self alloc]initWithPosition:pos]autorelease]; +} + +-(id) initWithPosition: (CGPoint) pos +{ + if( (self=[super init]) ) + position = pos; + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithPosition: position]; + return copy; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + ((CCNode *)target_).position = position; +} + +@end + +// +// CallFunc +// +#pragma mark CCCallFunc + +@implementation CCCallFunc + +@synthesize targetCallback = targetCallback_; + ++(id) actionWithTarget: (id) t selector:(SEL) s +{ + return [[[self alloc] initWithTarget: t selector: s] autorelease]; +} + +-(id) initWithTarget: (id) t selector:(SEL) s +{ + if( (self=[super init]) ) { + self.targetCallback = t; + selector_ = s; + } + return self; +} + +-(NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i | target = %@ | selector = %@>", + [self class], + self, + tag_, + [targetCallback_ class], + NSStringFromSelector(selector_) + ]; +} + +-(void) dealloc +{ + [targetCallback_ release]; + [super dealloc]; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithTarget:targetCallback_ selector:selector_]; + return copy; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + [self execute]; +} + +-(void) execute +{ + [targetCallback_ performSelector:selector_]; +} +@end + +// +// CallFuncN +// +#pragma mark CCCallFuncN + +@implementation CCCallFuncN + +-(void) execute +{ + [targetCallback_ performSelector:selector_ withObject:target_]; +} +@end + +// +// CallFuncND +// +#pragma mark CCCallFuncND + +@implementation CCCallFuncND + +@synthesize callbackMethod = callbackMethod_; + ++(id) actionWithTarget:(id)t selector:(SEL)s data:(void*)d +{ + return [[[self alloc] initWithTarget:t selector:s data:d] autorelease]; +} + +-(id) initWithTarget:(id)t selector:(SEL)s data:(void*)d +{ + if( (self=[super initWithTarget:t selector:s]) ) { + data_ = d; + +#if COCOS2D_DEBUG + NSMethodSignature * sig = [t methodSignatureForSelector:s]; // added + NSAssert(sig !=0 , @"Signature not found for selector - does it have the following form? -(void)name:(id)sender data:(void*)data"); +#endif + callbackMethod_ = (CC_CALLBACK_ND) [t methodForSelector:s]; + } + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithTarget:targetCallback_ selector:selector_ data:data_]; + return copy; +} + +-(void) dealloc +{ + // nothing to dealloc really. Everything is dealloc on super (CCCallFuncN) + [super dealloc]; +} + +-(void) execute +{ + callbackMethod_(targetCallback_,selector_,target_, data_); +} +@end + +@implementation CCCallFuncO +@synthesize object = object_; + ++(id) actionWithTarget: (id) t selector:(SEL) s object:(id)object +{ + return [[[self alloc] initWithTarget:t selector:s object:object] autorelease]; +} + +-(id) initWithTarget:(id) t selector:(SEL) s object:(id)object +{ + if( (self=[super initWithTarget:t selector:s] ) ) + self.object = object; + + return self; +} + +- (void) dealloc +{ + [object_ release]; + [super dealloc]; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithTarget:targetCallback_ selector:selector_ object:object_]; + return copy; +} + + +-(void) execute +{ + [targetCallback_ performSelector:selector_ withObject:object_]; +} + +@end + + +#pragma mark - +#pragma mark Blocks + +#if NS_BLOCKS_AVAILABLE + +#pragma mark CCCallBlock + +@implementation CCCallBlock + ++(id) actionWithBlock:(void(^)())block +{ + return [[[self alloc] initWithBlock:block] autorelease]; +} + +-(id) initWithBlock:(void(^)())block +{ + if ((self = [super init])) + block_ = [block copy]; + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithBlock:block_]; + return copy; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + [self execute]; +} + +-(void) execute +{ + block_(); +} + +-(void) dealloc +{ + [block_ release]; + [super dealloc]; +} + +@end + +#pragma mark CCCallBlockN + +@implementation CCCallBlockN + ++(id) actionWithBlock:(void(^)(CCNode *node))block +{ + return [[[self alloc] initWithBlock:block] autorelease]; +} + +-(id) initWithBlock:(void(^)(CCNode *node))block +{ + if ((self = [super init])) + block_ = [block copy]; + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithBlock:block_]; + return copy; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + [self execute]; +} + +-(void) execute +{ + block_(target_); +} + +-(void) dealloc +{ + [block_ release]; + [super dealloc]; +} + +@end + + +#endif // NS_BLOCKS_AVAILABLE diff --git a/libs/cocos2d/CCActionInterval.h b/libs/cocos2d/CCActionInterval.h new file mode 100644 index 0000000..b738662 --- /dev/null +++ b/libs/cocos2d/CCActionInterval.h @@ -0,0 +1,392 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2011 Ricardo Quesada + * + * 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 "CCAction.h" +#import "CCProtocols.h" + +#include + +/** An interval action is an action that takes place within a certain period of time. +It has an start time, and a finish time. The finish time is the parameter +duration plus the start time. + +These CCActionInterval actions have some interesting properties, like: + - They can run normally (default) + - They can run reversed with the reverse method + - They can run with the time altered with the Accelerate, AccelDeccel and Speed actions. + +For example, you can simulate a Ping Pong effect running the action normally and +then running it again in Reverse mode. + +Example: + + CCAction * pingPongAction = [CCSequence actions: action, [action reverse], nil]; +*/ +@interface CCActionInterval: CCFiniteTimeAction +{ + ccTime elapsed_; + BOOL firstTick_; +} + +/** how many seconds had elapsed since the actions started to run. */ +@property (nonatomic,readonly) ccTime elapsed; + +/** creates the action */ ++(id) actionWithDuration: (ccTime) d; +/** initializes the action */ +-(id) initWithDuration: (ccTime) d; +/** returns YES if the action has finished */ +-(BOOL) isDone; +/** returns a reversed action */ +- (CCActionInterval*) reverse; +@end + +/** Runs actions sequentially, one after another + */ +@interface CCSequence : CCActionInterval +{ + CCFiniteTimeAction *actions_[2]; + ccTime split_; + int last_; +} +/** helper contructor to create an array of sequenceable actions */ ++(id) actions: (CCFiniteTimeAction*) action1, ... NS_REQUIRES_NIL_TERMINATION; +/** helper contructor to create an array of sequenceable actions given an array */ ++(id) actionsWithArray: (NSArray*) actions; +/** creates the action */ ++(id) actionOne:(CCFiniteTimeAction*)actionOne two:(CCFiniteTimeAction*)actionTwo; +/** initializes the action */ +-(id) initOne:(CCFiniteTimeAction*)actionOne two:(CCFiniteTimeAction*)actionTwo; +@end + + +/** Repeats an action a number of times. + * To repeat an action forever use the CCRepeatForever action. + */ +@interface CCRepeat : CCActionInterval +{ + NSUInteger times_; + NSUInteger total_; + CCFiniteTimeAction *innerAction_; +} + +/** Inner action */ +@property (nonatomic,readwrite,retain) CCFiniteTimeAction *innerAction; + +/** creates a CCRepeat action. Times is an unsigned integer between 1 and MAX_UINT */ ++(id) actionWithAction:(CCFiniteTimeAction*)action times: (NSUInteger)times; +/** initializes a CCRepeat action. Times is an unsigned integer between 1 and MAX_UINT */ +-(id) initWithAction:(CCFiniteTimeAction*)action times: (NSUInteger)times; +@end + +/** Spawn a new action immediately + */ +@interface CCSpawn : CCActionInterval +{ + CCFiniteTimeAction *one_; + CCFiniteTimeAction *two_; +} +/** helper constructor to create an array of spawned actions */ ++(id) actions: (CCFiniteTimeAction*) action1, ... NS_REQUIRES_NIL_TERMINATION; +/** helper contructor to create an array of spawned actions given an array */ ++(id) actionsWithArray: (NSArray*) actions; +/** creates the Spawn action */ ++(id) actionOne: (CCFiniteTimeAction*) one two:(CCFiniteTimeAction*) two; +/** initializes the Spawn action with the 2 actions to spawn */ +-(id) initOne: (CCFiniteTimeAction*) one two:(CCFiniteTimeAction*) two; +@end + +/** Rotates a CCNode object to a certain angle by modifying it's + rotation attribute. + The direction will be decided by the shortest angle. +*/ +@interface CCRotateTo : CCActionInterval +{ + float dstAngle; + float startAngle; + float diffAngle; +} +/** creates the action */ ++(id) actionWithDuration:(ccTime)duration angle:(float)angle; +/** initializes the action */ +-(id) initWithDuration:(ccTime)duration angle:(float)angle; +@end + +/** Rotates a CCNode object clockwise a number of degrees by modiying it's rotation attribute. +*/ +@interface CCRotateBy : CCActionInterval +{ + float angle; + float startAngle; +} +/** creates the action */ ++(id) actionWithDuration:(ccTime)duration angle:(float)deltaAngle; +/** initializes the action */ +-(id) initWithDuration:(ccTime)duration angle:(float)deltaAngle; +@end + +/** Moves a CCNode object to the position x,y. x and y are absolute coordinates by modifying it's position attribute. +*/ +@interface CCMoveTo : CCActionInterval +{ + CGPoint endPosition; + CGPoint startPosition; + CGPoint delta; +} +/** creates the action */ ++(id) actionWithDuration:(ccTime)duration position:(CGPoint)position; +/** initializes the action */ +-(id) initWithDuration:(ccTime)duration position:(CGPoint)position; +@end + +/** Moves a CCNode object x,y pixels by modifying it's position attribute. + x and y are relative to the position of the object. + Duration is is seconds. +*/ +@interface CCMoveBy : CCMoveTo +{ +} +/** creates the action */ ++(id) actionWithDuration: (ccTime)duration position:(CGPoint)deltaPosition; +/** initializes the action */ +-(id) initWithDuration: (ccTime)duration position:(CGPoint)deltaPosition; +@end + +/** Moves a CCNode object simulating a parabolic jump movement by modifying it's position attribute. +*/ + @interface CCJumpBy : CCActionInterval +{ + CGPoint startPosition; + CGPoint delta; + ccTime height; + int jumps; +} +/** creates the action */ ++(id) actionWithDuration: (ccTime)duration position:(CGPoint)position height:(ccTime)height jumps:(int)jumps; +/** initializes the action */ +-(id) initWithDuration: (ccTime)duration position:(CGPoint)position height:(ccTime)height jumps:(int)jumps; +@end + +/** Moves a CCNode object to a parabolic position simulating a jump movement by modifying it's position attribute. +*/ + @interface CCJumpTo : CCJumpBy +{ +} +@end + +/** bezier configuration structure + */ +typedef struct _ccBezierConfig { + //! end position of the bezier + CGPoint endPosition; + //! Bezier control point 1 + CGPoint controlPoint_1; + //! Bezier control point 2 + CGPoint controlPoint_2; +} ccBezierConfig; + +/** An action that moves the target with a cubic Bezier curve by a certain distance. + */ +@interface CCBezierBy : CCActionInterval +{ + ccBezierConfig config; + CGPoint startPosition; +} + +/** creates the action with a duration and a bezier configuration */ ++(id) actionWithDuration: (ccTime) t bezier:(ccBezierConfig) c; + +/** initializes the action with a duration and a bezier configuration */ +-(id) initWithDuration: (ccTime) t bezier:(ccBezierConfig) c; +@end + +/** An action that moves the target with a cubic Bezier curve to a destination point. + @since v0.8.2 + */ +@interface CCBezierTo : CCBezierBy +{ +} +@end + +/** Scales a CCNode object to a zoom factor by modifying it's scale attribute. + @warning This action doesn't support "reverse" + */ +@interface CCScaleTo : CCActionInterval +{ + float scaleX; + float scaleY; + float startScaleX; + float startScaleY; + float endScaleX; + float endScaleY; + float deltaX; + float deltaY; +} +/** creates the action with the same scale factor for X and Y */ ++(id) actionWithDuration: (ccTime)duration scale:(float) s; +/** initializes the action with the same scale factor for X and Y */ +-(id) initWithDuration: (ccTime)duration scale:(float) s; +/** creates the action with and X factor and a Y factor */ ++(id) actionWithDuration: (ccTime)duration scaleX:(float) sx scaleY:(float)sy; +/** initializes the action with and X factor and a Y factor */ +-(id) initWithDuration: (ccTime)duration scaleX:(float) sx scaleY:(float)sy; +@end + +/** Scales a CCNode object a zoom factor by modifying it's scale attribute. +*/ +@interface CCScaleBy : CCScaleTo +{ +} +@end + +/** Blinks a CCNode object by modifying it's visible attribute +*/ +@interface CCBlink : CCActionInterval +{ + NSUInteger times_; +} +/** creates the action */ ++(id) actionWithDuration: (ccTime)duration blinks:(NSUInteger)blinks; +/** initilizes the action */ +-(id) initWithDuration: (ccTime)duration blinks:(NSUInteger)blinks; +@end + +/** Fades In an object that implements the CCRGBAProtocol protocol. It modifies the opacity from 0 to 255. + The "reverse" of this action is FadeOut + */ +@interface CCFadeIn : CCActionInterval +{ +} +@end + +/** Fades Out an object that implements the CCRGBAProtocol protocol. It modifies the opacity from 255 to 0. + The "reverse" of this action is FadeIn +*/ +@interface CCFadeOut : CCActionInterval +{ +} +@end + +/** Fades an object that implements the CCRGBAProtocol protocol. It modifies the opacity from the current value to a custom one. + @warning This action doesn't support "reverse" + */ +@interface CCFadeTo : CCActionInterval +{ + GLubyte toOpacity; + GLubyte fromOpacity; +} +/** creates an action with duration and opactiy */ ++(id) actionWithDuration:(ccTime)duration opacity:(GLubyte)opactiy; +/** initializes the action with duration and opacity */ +-(id) initWithDuration:(ccTime)duration opacity:(GLubyte)opacity; +@end + +/** Tints a CCNode that implements the CCNodeRGB protocol from current tint to a custom one. + @warning This action doesn't support "reverse" + @since v0.7.2 +*/ +@interface CCTintTo : CCActionInterval +{ + ccColor3B to; + ccColor3B from; +} +/** creates an action with duration and color */ ++(id) actionWithDuration:(ccTime)duration red:(GLubyte)red green:(GLubyte)green blue:(GLubyte)blue; +/** initializes the action with duration and color */ +-(id) initWithDuration:(ccTime)duration red:(GLubyte)red green:(GLubyte)green blue:(GLubyte)blue; +@end + +/** Tints a CCNode that implements the CCNodeRGB protocol from current tint to a custom one. + @since v0.7.2 + */ +@interface CCTintBy : CCActionInterval +{ + GLshort deltaR, deltaG, deltaB; + GLshort fromR, fromG, fromB; +} +/** creates an action with duration and color */ ++(id) actionWithDuration:(ccTime)duration red:(GLshort)deltaRed green:(GLshort)deltaGreen blue:(GLshort)deltaBlue; +/** initializes the action with duration and color */ +-(id) initWithDuration:(ccTime)duration red:(GLshort)deltaRed green:(GLshort)deltaGreen blue:(GLshort)deltaBlue; +@end + +/** Delays the action a certain amount of seconds +*/ +@interface CCDelayTime : CCActionInterval +{ +} +@end + +/** Executes an action in reverse order, from time=duration to time=0 + + @warning Use this action carefully. This action is not + sequenceable. Use it as the default "reversed" method + of your own actions, but using it outside the "reversed" + scope is not recommended. +*/ +@interface CCReverseTime : CCActionInterval +{ + CCFiniteTimeAction * other_; +} +/** creates the action */ ++(id) actionWithAction: (CCFiniteTimeAction*) action; +/** initializes the action */ +-(id) initWithAction: (CCFiniteTimeAction*) action; +@end + + +@class CCAnimation; +@class CCTexture2D; +/** Animates a sprite given the name of an Animation */ +@interface CCAnimate : CCActionInterval +{ + CCAnimation *animation_; + id origFrame; + BOOL restoreOriginalFrame; +} +/** animation used for the animage */ +@property (readwrite,nonatomic,retain) CCAnimation * animation; + +/** creates the action with an Animation and will restore the original frame when the animation is over */ ++(id) actionWithAnimation:(CCAnimation*) a; +/** initializes the action with an Animation and will restore the original frame when the animtion is over */ +-(id) initWithAnimation:(CCAnimation*) a; +/** creates the action with an Animation */ ++(id) actionWithAnimation:(CCAnimation*) a restoreOriginalFrame:(BOOL)b; +/** initializes the action with an Animation */ +-(id) initWithAnimation:(CCAnimation*) a restoreOriginalFrame:(BOOL)b; +/** creates an action with a duration, animation and depending of the restoreOriginalFrame, it will restore the original frame or not. + The 'delay' parameter of the animation will be overrided by the duration parameter. + @since v0.99.0 + */ ++(id) actionWithDuration:(ccTime)duration animation:(CCAnimation*)animation restoreOriginalFrame:(BOOL)b; +/** initializes an action with a duration, animation and depending of the restoreOriginalFrame, it will restore the original frame or not. + The 'delay' parameter of the animation will be overrided by the duration parameter. + @since v0.99.0 + */ +-(id) initWithDuration:(ccTime)duration animation:(CCAnimation*)animation restoreOriginalFrame:(BOOL)b; +@end diff --git a/libs/cocos2d/CCActionInterval.m b/libs/cocos2d/CCActionInterval.m new file mode 100644 index 0000000..73028a8 --- /dev/null +++ b/libs/cocos2d/CCActionInterval.m @@ -0,0 +1,1250 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2011 Ricardo Quesada + * + * 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 "CCActionInterval.h" +#import "CCSprite.h" +#import "CCSpriteFrame.h" +#import "CCAnimation.h" +#import "CCNode.h" +#import "Support/CGPointExtension.h" + +// +// IntervalAction +// +#pragma mark - +#pragma mark IntervalAction +@implementation CCActionInterval + +@synthesize elapsed = elapsed_; + +-(id) init +{ + NSAssert(NO, @"IntervalActionInit: Init not supported. Use InitWithDuration"); + [self release]; + return nil; +} + ++(id) actionWithDuration: (ccTime) d +{ + return [[[self alloc] initWithDuration:d ] autorelease]; +} + +-(id) initWithDuration: (ccTime) d +{ + if( (self=[super init]) ) { + duration_ = d; + + // prevent division by 0 + // This comparison could be in step:, but it might decrease the performance + // by 3% in heavy based action games. + if( duration_ == 0 ) + duration_ = FLT_EPSILON; + elapsed_ = 0; + firstTick_ = YES; + } + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] ]; + return copy; +} + +- (BOOL) isDone +{ + return (elapsed_ >= duration_); +} + +-(void) step: (ccTime) dt +{ + if( firstTick_ ) { + firstTick_ = NO; + elapsed_ = 0; + } else + elapsed_ += dt; + + [self update: MIN(1, elapsed_/duration_)]; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + elapsed_ = 0.0f; + firstTick_ = YES; +} + +- (CCActionInterval*) reverse +{ + NSAssert(NO, @"CCIntervalAction: reverse not implemented."); + return nil; +} +@end + +// +// Sequence +// +#pragma mark - +#pragma mark Sequence +@implementation CCSequence ++(id) actions: (CCFiniteTimeAction*) action1, ... +{ + va_list params; + va_start(params,action1); + + CCFiniteTimeAction *now; + CCFiniteTimeAction *prev = action1; + + while( action1 ) { + now = va_arg(params,CCFiniteTimeAction*); + if ( now ) + prev = [self actionOne: prev two: now]; + else + break; + } + va_end(params); + return prev; +} + ++(id) actionsWithArray: (NSArray*) actions +{ + CCFiniteTimeAction *prev = [actions objectAtIndex:0]; + + for (int i = 1; i < [actions count]; i++) + prev = [self actionOne:prev two:[actions objectAtIndex:i]]; + + return prev; +} + ++(id) actionOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two +{ + return [[[self alloc] initOne:one two:two ] autorelease]; +} + +-(id) initOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two +{ + NSAssert( one!=nil && two!=nil, @"Sequence: arguments must be non-nil"); + NSAssert( one!=actions_[0] && one!=actions_[1], @"Sequence: re-init using the same parameters is not supported"); + NSAssert( two!=actions_[1] && two!=actions_[0], @"Sequence: re-init using the same parameters is not supported"); + + ccTime d = [one duration] + [two duration]; + + if( (self=[super initWithDuration: d]) ) { + + // XXX: Supports re-init without leaking. Fails if one==one_ || two==two_ + [actions_[0] release]; + [actions_[1] release]; + + actions_[0] = [one retain]; + actions_[1] = [two retain]; + } + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone:zone] initOne:[[actions_[0] copy] autorelease] two:[[actions_[1] copy] autorelease] ]; + return copy; +} + +-(void) dealloc +{ + [actions_[0] release]; + [actions_[1] release]; + [super dealloc]; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + split_ = [actions_[0] duration] / duration_; + last_ = -1; +} + +-(void) stop +{ + [actions_[0] stop]; + [actions_[1] stop]; + [super stop]; +} + +-(void) update: (ccTime) t +{ + int found = 0; + ccTime new_t = 0.0f; + + if( t >= split_ ) { + found = 1; + if ( split_ == 1 ) + new_t = 1; + else + new_t = (t-split_) / (1 - split_ ); + } else { + found = 0; + if( split_ != 0 ) + new_t = t / split_; + else + new_t = 1; + } + + if (last_ == -1 && found==1) { + [actions_[0] startWithTarget:target_]; + [actions_[0] update:1.0f]; + [actions_[0] stop]; + } + + if (last_ != found ) { + if( last_ != -1 ) { + [actions_[last_] update: 1.0f]; + [actions_[last_] stop]; + } + [actions_[found] startWithTarget:target_]; + } + [actions_[found] update: new_t]; + last_ = found; +} + +- (CCActionInterval *) reverse +{ + return [[self class] actionOne: [actions_[1] reverse] two: [actions_[0] reverse ] ]; +} +@end + +// +// Repeat +// +#pragma mark - +#pragma mark CCRepeat +@implementation CCRepeat +@synthesize innerAction=innerAction_; + ++(id) actionWithAction:(CCFiniteTimeAction*)action times:(NSUInteger)times +{ + return [[[self alloc] initWithAction:action times:times] autorelease]; +} + +-(id) initWithAction:(CCFiniteTimeAction*)action times:(NSUInteger)times +{ + ccTime d = [action duration] * times; + + if( (self=[super initWithDuration: d ]) ) { + times_ = times; + self.innerAction = action; + + total_ = 0; + } + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone:zone] initWithAction:[[innerAction_ copy] autorelease] times:times_]; + return copy; +} + +-(void) dealloc +{ + [innerAction_ release]; + [super dealloc]; +} + +-(void) startWithTarget:(id)aTarget +{ + total_ = 0; + [super startWithTarget:aTarget]; + [innerAction_ startWithTarget:aTarget]; +} + +-(void) stop +{ + [innerAction_ stop]; + [super stop]; +} + + +// issue #80. Instead of hooking step:, hook update: since it can be called by any +// container action like Repeat, Sequence, AccelDeccel, etc.. +-(void) update:(ccTime) dt +{ + ccTime t = dt * times_; + if( t > total_+1 ) { + [innerAction_ update:1.0f]; + total_++; + [innerAction_ stop]; + [innerAction_ startWithTarget:target_]; + + // repeat is over ? + if( total_== times_ ) + // so, set it in the original position + [innerAction_ update:0]; + else { + // no ? start next repeat with the right update + // to prevent jerk (issue #390) + [innerAction_ update: t-total_]; + } + + } else { + + float r = fmodf(t, 1.0f); + + // fix last repeat position + // else it could be 0. + if( dt== 1.0f) { + r = 1.0f; + total_++; // this is the added line + } + [innerAction_ update: MIN(r,1)]; + } +} + +-(BOOL) isDone +{ + return ( total_ == times_ ); +} + +- (CCActionInterval *) reverse +{ + return [[self class] actionWithAction:[innerAction_ reverse] times:times_]; +} +@end + +// +// Spawn +// +#pragma mark - +#pragma mark Spawn + +@implementation CCSpawn ++(id) actions: (CCFiniteTimeAction*) action1, ... +{ + va_list params; + va_start(params,action1); + + CCFiniteTimeAction *now; + CCFiniteTimeAction *prev = action1; + + while( action1 ) { + now = va_arg(params,CCFiniteTimeAction*); + if ( now ) + prev = [self actionOne: prev two: now]; + else + break; + } + va_end(params); + return prev; +} + ++(id) actionsWithArray: (NSArray*) actions +{ + CCFiniteTimeAction *prev = [actions objectAtIndex:0]; + + for (int i = 1; i < [actions count]; i++) + prev = [self actionOne:prev two:[actions objectAtIndex:i]]; + + return prev; +} + ++(id) actionOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two +{ + return [[[self alloc] initOne:one two:two ] autorelease]; +} + +-(id) initOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two +{ + NSAssert( one!=nil && two!=nil, @"Spawn: arguments must be non-nil"); + NSAssert( one!=one_ && one!=two_, @"Spawn: reinit using same parameters is not supported"); + NSAssert( two!=two_ && two!=one_, @"Spawn: reinit using same parameters is not supported"); + + ccTime d1 = [one duration]; + ccTime d2 = [two duration]; + + [super initWithDuration: MAX(d1,d2)]; + + // XXX: Supports re-init without leaking. Fails if one==one_ || two==two_ + [one_ release]; + [two_ release]; + + one_ = one; + two_ = two; + + if( d1 > d2 ) + two_ = [CCSequence actionOne:two two:[CCDelayTime actionWithDuration: (d1-d2)] ]; + else if( d1 < d2) + one_ = [CCSequence actionOne:one two: [CCDelayTime actionWithDuration: (d2-d1)] ]; + + [one_ retain]; + [two_ retain]; + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] initOne: [[one_ copy] autorelease] two: [[two_ copy] autorelease] ]; + return copy; +} + +-(void) dealloc +{ + [one_ release]; + [two_ release]; + [super dealloc]; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + [one_ startWithTarget:target_]; + [two_ startWithTarget:target_]; +} + +-(void) stop +{ + [one_ stop]; + [two_ stop]; + [super stop]; +} + +-(void) update: (ccTime) t +{ + [one_ update:t]; + [two_ update:t]; +} + +- (CCActionInterval *) reverse +{ + return [[self class] actionOne: [one_ reverse] two: [two_ reverse ] ]; +} +@end + +// +// RotateTo +// +#pragma mark - +#pragma mark RotateTo + +@implementation CCRotateTo ++(id) actionWithDuration: (ccTime) t angle:(float) a +{ + return [[[self alloc] initWithDuration:t angle:a ] autorelease]; +} + +-(id) initWithDuration: (ccTime) t angle:(float) a +{ + if( (self=[super initWithDuration: t]) ) + dstAngle = a; + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:[self duration] angle: dstAngle]; + return copy; +} + +-(void) startWithTarget:(CCNode *)aTarget +{ + [super startWithTarget:aTarget]; + + startAngle = [target_ rotation]; + if (startAngle > 0) + startAngle = fmodf(startAngle, 360.0f); + else + startAngle = fmodf(startAngle, -360.0f); + + diffAngle = dstAngle - startAngle; + if (diffAngle > 180) + diffAngle -= 360; + if (diffAngle < -180) + diffAngle += 360; +} +-(void) update: (ccTime) t +{ + [target_ setRotation: startAngle + diffAngle * t]; +} +@end + + +// +// RotateBy +// +#pragma mark - +#pragma mark RotateBy + +@implementation CCRotateBy ++(id) actionWithDuration: (ccTime) t angle:(float) a +{ + return [[[self alloc] initWithDuration:t angle:a ] autorelease]; +} + +-(id) initWithDuration: (ccTime) t angle:(float) a +{ + if( (self=[super initWithDuration: t]) ) + angle = a; + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] angle: angle]; + return copy; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + startAngle = [target_ rotation]; +} + +-(void) update: (ccTime) t +{ + // XXX: shall I add % 360 + [target_ setRotation: (startAngle + angle * t )]; +} + +-(CCActionInterval*) reverse +{ + return [[self class] actionWithDuration:duration_ angle:-angle]; +} + +@end + +// +// MoveTo +// +#pragma mark - +#pragma mark MoveTo + +@implementation CCMoveTo ++(id) actionWithDuration: (ccTime) t position: (CGPoint) p +{ + return [[[self alloc] initWithDuration:t position:p ] autorelease]; +} + +-(id) initWithDuration: (ccTime) t position: (CGPoint) p +{ + if( (self=[super initWithDuration: t]) ) + endPosition = p; + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] position: endPosition]; + return copy; +} + +-(void) startWithTarget:(CCNode *)aTarget +{ + [super startWithTarget:aTarget]; + startPosition = [(CCNode*)target_ position]; + delta = ccpSub( endPosition, startPosition ); +} + +-(void) update: (ccTime) t +{ + [target_ setPosition: ccp( (startPosition.x + delta.x * t ), (startPosition.y + delta.y * t ) )]; +} +@end + +// +// MoveBy +// +#pragma mark - +#pragma mark MoveBy + +@implementation CCMoveBy ++(id) actionWithDuration: (ccTime) t position: (CGPoint) p +{ + return [[[self alloc] initWithDuration:t position:p ] autorelease]; +} + +-(id) initWithDuration: (ccTime) t position: (CGPoint) p +{ + if( (self=[super initWithDuration: t]) ) + delta = p; + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] position: delta]; + return copy; +} + +-(void) startWithTarget:(CCNode *)aTarget +{ + CGPoint dTmp = delta; + [super startWithTarget:aTarget]; + delta = dTmp; +} + +-(CCActionInterval*) reverse +{ + return [[self class] actionWithDuration:duration_ position:ccp( -delta.x, -delta.y)]; +} +@end + +// +// JumpBy +// +#pragma mark - +#pragma mark JumpBy + +@implementation CCJumpBy ++(id) actionWithDuration: (ccTime) t position: (CGPoint) pos height: (ccTime) h jumps:(int)j +{ + return [[[self alloc] initWithDuration: t position: pos height: h jumps:j] autorelease]; +} + +-(id) initWithDuration: (ccTime) t position: (CGPoint) pos height: (ccTime) h jumps:(int)j +{ + if( (self=[super initWithDuration:t]) ) { + delta = pos; + height = h; + jumps = j; + } + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] position: delta height:height jumps:jumps]; + return copy; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + startPosition = [(CCNode*)target_ position]; +} + +-(void) update: (ccTime) t +{ + // Sin jump. Less realistic +// ccTime y = height * fabsf( sinf(t * (CGFloat)M_PI * jumps ) ); +// y += delta.y * t; +// ccTime x = delta.x * t; +// [target setPosition: ccp( startPosition.x + x, startPosition.y + y )]; + + // parabolic jump (since v0.8.2) + ccTime frac = fmodf( t * jumps, 1.0f ); + ccTime y = height * 4 * frac * (1 - frac); + y += delta.y * t; + ccTime x = delta.x * t; + [target_ setPosition: ccp( startPosition.x + x, startPosition.y + y )]; + +} + +-(CCActionInterval*) reverse +{ + return [[self class] actionWithDuration:duration_ position: ccp(-delta.x,-delta.y) height: height jumps:jumps]; +} +@end + +// +// JumpTo +// +#pragma mark - +#pragma mark JumpTo + +@implementation CCJumpTo +-(void) startWithTarget:(CCNode *)aTarget +{ + [super startWithTarget:aTarget]; + delta = ccp( delta.x - startPosition.x, delta.y - startPosition.y ); +} +@end + + +#pragma mark - +#pragma mark BezierBy + +// Bezier cubic formula: +// ((1 - t) + t)3 = 1 +// Expands to… +// (1 - t)3 + 3t(1-t)2 + 3t2(1 - t) + t3 = 1 +static inline float bezierat( float a, float b, float c, float d, ccTime t ) +{ + return (powf(1-t,3) * a + + 3*t*(powf(1-t,2))*b + + 3*powf(t,2)*(1-t)*c + + powf(t,3)*d ); +} + +// +// BezierBy +// +@implementation CCBezierBy ++(id) actionWithDuration: (ccTime) t bezier:(ccBezierConfig) c +{ + return [[[self alloc] initWithDuration:t bezier:c ] autorelease]; +} + +-(id) initWithDuration: (ccTime) t bezier:(ccBezierConfig) c +{ + if( (self=[super initWithDuration: t]) ) { + config = c; + } + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] bezier: config]; + return copy; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + startPosition = [(CCNode*)target_ position]; +} + +-(void) update: (ccTime) t +{ + float xa = 0; + float xb = config.controlPoint_1.x; + float xc = config.controlPoint_2.x; + float xd = config.endPosition.x; + + float ya = 0; + float yb = config.controlPoint_1.y; + float yc = config.controlPoint_2.y; + float yd = config.endPosition.y; + + float x = bezierat(xa, xb, xc, xd, t); + float y = bezierat(ya, yb, yc, yd, t); + [target_ setPosition: ccpAdd( startPosition, ccp(x,y))]; +} + +- (CCActionInterval*) reverse +{ + ccBezierConfig r; + + r.endPosition = ccpNeg(config.endPosition); + r.controlPoint_1 = ccpAdd(config.controlPoint_2, ccpNeg(config.endPosition)); + r.controlPoint_2 = ccpAdd(config.controlPoint_1, ccpNeg(config.endPosition)); + + CCBezierBy *action = [[self class] actionWithDuration:[self duration] bezier:r]; + return action; +} +@end + +// +// BezierTo +// +#pragma mark - +#pragma mark BezierTo +@implementation CCBezierTo +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + config.controlPoint_1 = ccpSub(config.controlPoint_1, startPosition); + config.controlPoint_2 = ccpSub(config.controlPoint_2, startPosition); + config.endPosition = ccpSub(config.endPosition, startPosition); +} +@end + + +// +// ScaleTo +// +#pragma mark - +#pragma mark ScaleTo +@implementation CCScaleTo ++(id) actionWithDuration: (ccTime) t scale:(float) s +{ + return [[[self alloc] initWithDuration: t scale:s] autorelease]; +} + +-(id) initWithDuration: (ccTime) t scale:(float) s +{ + if( (self=[super initWithDuration: t]) ) { + endScaleX = s; + endScaleY = s; + } + return self; +} + ++(id) actionWithDuration: (ccTime) t scaleX:(float)sx scaleY:(float)sy +{ + return [[[self alloc] initWithDuration: t scaleX:sx scaleY:sy] autorelease]; +} + +-(id) initWithDuration: (ccTime) t scaleX:(float)sx scaleY:(float)sy +{ + if( (self=[super initWithDuration: t]) ) { + endScaleX = sx; + endScaleY = sy; + } + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] scaleX:endScaleX scaleY:endScaleY]; + return copy; +} + +-(void) startWithTarget:(CCNode *)aTarget +{ + [super startWithTarget:aTarget]; + startScaleX = [target_ scaleX]; + startScaleY = [target_ scaleY]; + deltaX = endScaleX - startScaleX; + deltaY = endScaleY - startScaleY; +} + +-(void) update: (ccTime) t +{ + [target_ setScaleX: (startScaleX + deltaX * t ) ]; + [target_ setScaleY: (startScaleY + deltaY * t ) ]; +} +@end + +// +// ScaleBy +// +#pragma mark - +#pragma mark ScaleBy +@implementation CCScaleBy +-(void) startWithTarget:(CCNode *)aTarget +{ + [super startWithTarget:aTarget]; + deltaX = startScaleX * endScaleX - startScaleX; + deltaY = startScaleY * endScaleY - startScaleY; +} + +-(CCActionInterval*) reverse +{ + return [[self class] actionWithDuration:duration_ scaleX: 1/endScaleX scaleY:1/endScaleY]; +} +@end + +// +// Blink +// +#pragma mark - +#pragma mark Blink +@implementation CCBlink ++(id) actionWithDuration: (ccTime) t blinks: (NSUInteger) b +{ + return [[[ self alloc] initWithDuration: t blinks: b] autorelease]; +} + +-(id) initWithDuration: (ccTime) t blinks: (NSUInteger) b +{ + if( (self=[super initWithDuration: t] ) ) + times_ = b; + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] blinks: times_]; + return copy; +} + +-(void) update: (ccTime) t +{ + if( ! [self isDone] ) { + ccTime slice = 1.0f / times_; + ccTime m = fmodf(t, slice); + [target_ setVisible: (m > slice/2) ? YES : NO]; + } +} + +-(CCActionInterval*) reverse +{ + // return 'self' + return [[self class] actionWithDuration:duration_ blinks: times_]; +} +@end + +// +// FadeIn +// +#pragma mark - +#pragma mark FadeIn +@implementation CCFadeIn +-(void) update: (ccTime) t +{ + [(id) target_ setOpacity: 255 *t]; +} + +-(CCActionInterval*) reverse +{ + return [CCFadeOut actionWithDuration:duration_]; +} +@end + +// +// FadeOut +// +#pragma mark - +#pragma mark FadeOut +@implementation CCFadeOut +-(void) update: (ccTime) t +{ + [(id) target_ setOpacity: 255 *(1-t)]; +} + +-(CCActionInterval*) reverse +{ + return [CCFadeIn actionWithDuration:duration_]; +} +@end + +// +// FadeTo +// +#pragma mark - +#pragma mark FadeTo +@implementation CCFadeTo ++(id) actionWithDuration: (ccTime) t opacity: (GLubyte) o +{ + return [[[ self alloc] initWithDuration: t opacity: o] autorelease]; +} + +-(id) initWithDuration: (ccTime) t opacity: (GLubyte) o +{ + if( (self=[super initWithDuration: t] ) ) + toOpacity = o; + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] opacity: toOpacity]; + return copy; +} + +-(void) startWithTarget:(CCNode *)aTarget +{ + [super startWithTarget:aTarget]; + fromOpacity = [(id)target_ opacity]; +} + +-(void) update: (ccTime) t +{ + [(id)target_ setOpacity: fromOpacity + ( toOpacity - fromOpacity ) * t]; +} +@end + +// +// TintTo +// +#pragma mark - +#pragma mark TintTo +@implementation CCTintTo ++(id) actionWithDuration:(ccTime)t red:(GLubyte)r green:(GLubyte)g blue:(GLubyte)b +{ + return [[(CCTintTo*)[ self alloc] initWithDuration:t red:r green:g blue:b] autorelease]; +} + +-(id) initWithDuration: (ccTime) t red:(GLubyte)r green:(GLubyte)g blue:(GLubyte)b +{ + if( (self=[super initWithDuration: t] ) ) + to = ccc3(r,g,b); + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [(CCTintTo*)[[self class] allocWithZone: zone] initWithDuration: [self duration] red:to.r green:to.g blue:to.b]; + return copy; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + + id tn = (id) target_; + from = [tn color]; +} + +-(void) update: (ccTime) t +{ + id tn = (id) target_; + [tn setColor:ccc3(from.r + (to.r - from.r) * t, from.g + (to.g - from.g) * t, from.b + (to.b - from.b) * t)]; +} +@end + +// +// TintBy +// +#pragma mark - +#pragma mark TintBy +@implementation CCTintBy ++(id) actionWithDuration:(ccTime)t red:(GLshort)r green:(GLshort)g blue:(GLshort)b +{ + return [[(CCTintBy*)[ self alloc] initWithDuration:t red:r green:g blue:b] autorelease]; +} + +-(id) initWithDuration:(ccTime)t red:(GLshort)r green:(GLshort)g blue:(GLshort)b +{ + if( (self=[super initWithDuration: t] ) ) { + deltaR = r; + deltaG = g; + deltaB = b; + } + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + return[(CCTintBy*)[[self class] allocWithZone: zone] initWithDuration: [self duration] red:deltaR green:deltaG blue:deltaB]; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + + id tn = (id) target_; + ccColor3B color = [tn color]; + fromR = color.r; + fromG = color.g; + fromB = color.b; +} + +-(void) update: (ccTime) t +{ + id tn = (id) target_; + [tn setColor:ccc3( fromR + deltaR * t, fromG + deltaG * t, fromB + deltaB * t)]; +} + +- (CCActionInterval*) reverse +{ + return [CCTintBy actionWithDuration:duration_ red:-deltaR green:-deltaG blue:-deltaB]; +} +@end + +// +// DelayTime +// +#pragma mark - +#pragma mark DelayTime +@implementation CCDelayTime +-(void) update: (ccTime) t +{ + return; +} + +-(id)reverse +{ + return [[self class] actionWithDuration:duration_]; +} +@end + +// +// ReverseTime +// +#pragma mark - +#pragma mark ReverseTime +@implementation CCReverseTime ++(id) actionWithAction: (CCFiniteTimeAction*) action +{ + // casting to prevent warnings + CCReverseTime *a = [super alloc]; + return [[a initWithAction:action] autorelease]; +} + +-(id) initWithAction: (CCFiniteTimeAction*) action +{ + NSAssert(action != nil, @"CCReverseTime: action should not be nil"); + NSAssert(action != other_, @"CCReverseTime: re-init doesn't support using the same arguments"); + + if( (self=[super initWithDuration: [action duration]]) ) { + // Don't leak if action is reused + [other_ release]; + other_ = [action retain]; + } + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + return [[[self class] allocWithZone: zone] initWithAction:[[other_ copy] autorelease] ]; +} + +-(void) dealloc +{ + [other_ release]; + [super dealloc]; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + [other_ startWithTarget:target_]; +} + +-(void) stop +{ + [other_ stop]; + [super stop]; +} + +-(void) update:(ccTime)t +{ + [other_ update:1-t]; +} + +-(CCActionInterval*) reverse +{ + return [[other_ copy] autorelease]; +} +@end + +// +// Animate +// + +#pragma mark - +#pragma mark Animate +@implementation CCAnimate + +@synthesize animation = animation_; + ++(id) actionWithAnimation: (CCAnimation*)anim +{ + return [[[self alloc] initWithAnimation:anim restoreOriginalFrame:YES] autorelease]; +} + ++(id) actionWithAnimation: (CCAnimation*)anim restoreOriginalFrame:(BOOL)b +{ + return [[[self alloc] initWithAnimation:anim restoreOriginalFrame:b] autorelease]; +} + ++(id) actionWithDuration:(ccTime)duration animation: (CCAnimation*)anim restoreOriginalFrame:(BOOL)b +{ + return [[[self alloc] initWithDuration:duration animation:anim restoreOriginalFrame:b] autorelease]; +} + +-(id) initWithAnimation: (CCAnimation*)anim +{ + NSAssert( anim!=nil, @"Animate: argument Animation must be non-nil"); + return [self initWithAnimation:anim restoreOriginalFrame:YES]; +} + +-(id) initWithAnimation: (CCAnimation*)anim restoreOriginalFrame:(BOOL) b +{ + NSAssert( anim!=nil, @"Animate: argument Animation must be non-nil"); + + if( (self=[super initWithDuration: [[anim frames] count] * [anim delay]]) ) { + + restoreOriginalFrame = b; + self.animation = anim; + origFrame = nil; + } + return self; +} + +-(id) initWithDuration:(ccTime)aDuration animation: (CCAnimation*)anim restoreOriginalFrame:(BOOL) b +{ + NSAssert( anim!=nil, @"Animate: argument Animation must be non-nil"); + + if( (self=[super initWithDuration:aDuration] ) ) { + + restoreOriginalFrame = b; + self.animation = anim; + origFrame = nil; + } + return self; +} + + +-(id) copyWithZone: (NSZone*) zone +{ + return [[[self class] allocWithZone: zone] initWithDuration:duration_ animation:animation_ restoreOriginalFrame:restoreOriginalFrame]; +} + +-(void) dealloc +{ + [animation_ release]; + [origFrame release]; + [super dealloc]; +} + +-(void) startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + CCSprite *sprite = target_; + + [origFrame release]; + + if( restoreOriginalFrame ) + origFrame = [[sprite displayedFrame] retain]; +} + +-(void) stop +{ + if( restoreOriginalFrame ) { + CCSprite *sprite = target_; + [sprite setDisplayFrame:origFrame]; + } + + [super stop]; +} + +-(void) update: (ccTime) t +{ + NSArray *frames = [animation_ frames]; + NSUInteger numberOfFrames = [frames count]; + + NSUInteger idx = t * numberOfFrames; + + if( idx >= numberOfFrames ) + idx = numberOfFrames -1; + + CCSprite *sprite = target_; + if (! [sprite isFrameDisplayed: [frames objectAtIndex: idx]] ) + [sprite setDisplayFrame: [frames objectAtIndex:idx]]; +} + +- (CCActionInterval *) reverse +{ + NSArray *oldArray = [animation_ frames]; + NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:[oldArray count]]; + NSEnumerator *enumerator = [oldArray reverseObjectEnumerator]; + for (id element in enumerator) + [newArray addObject:[[element copy] autorelease]]; + + CCAnimation *newAnim = [CCAnimation animationWithFrames:newArray delay:animation_.delay]; + return [[self class] actionWithDuration:duration_ animation:newAnim restoreOriginalFrame:restoreOriginalFrame]; +} + +@end diff --git a/libs/cocos2d/CCActionManager.h b/libs/cocos2d/CCActionManager.h new file mode 100644 index 0000000..560741a --- /dev/null +++ b/libs/cocos2d/CCActionManager.h @@ -0,0 +1,109 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2009 Valentin Milea + * + * 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 "CCAction.h" +#import "Support/ccCArray.h" +#import "Support/uthash.h" + +typedef struct _hashElement +{ + struct ccArray *actions; + id target; + NSUInteger actionIndex; + CCAction *currentAction; + BOOL currentActionSalvaged; + BOOL paused; + UT_hash_handle hh; +} tHashElement; + + +/** CCActionManager is a singleton that manages all the actions. + Normally you won't need to use this singleton directly. 99% of the cases you will use the CCNode interface, + which uses this singleton. + But there are some cases where you might need to use this singleton. + Examples: + - When you want to run an action where the target is different from a CCNode. + - When you want to pause / resume the actions + + @since v0.8 + */ +@interface CCActionManager : NSObject +{ + tHashElement *targets; + tHashElement *currentTarget; + BOOL currentTargetSalvaged; +} + +/** returns a shared instance of the CCActionManager */ ++ (CCActionManager *)sharedManager; + +/** purges the shared action manager. It releases the retained instance. + @since v0.99.0 + */ ++(void)purgeSharedManager; + +// actions + +/** Adds an action with a target. + If the target is already present, then the action will be added to the existing target. + If the target is not present, a new instance of this target will be created either paused or paused, and the action will be added to the newly created target. + When the target is paused, the queued actions won't be 'ticked'. + */ +-(void) addAction: (CCAction*) action target:(id)target paused:(BOOL)paused; +/** Removes all actions from all the targers. + */ +-(void) removeAllActions; + +/** Removes all actions from a certain target. + All the actions that belongs to the target will be removed. + */ +-(void) removeAllActionsFromTarget:(id)target; +/** Removes an action given an action reference. + */ +-(void) removeAction: (CCAction*) action; +/** Removes an action given its tag and the target */ +-(void) removeActionByTag:(NSInteger)tag target:(id)target; +/** Gets an action given its tag an a target + @return the Action the with the given tag + */ +-(CCAction*) getActionByTag:(NSInteger) tag target:(id)target; +/** Returns the numbers of actions that are running in a certain target + * Composable actions are counted as 1 action. Example: + * If you are running 1 Sequence of 7 actions, it will return 1. + * If you are running 7 Sequences of 2 actions, it will return 7. + */ +-(NSUInteger) numberOfRunningActionsInTarget:(id)target; + +/** Pauses the target: all running actions and newly added actions will be paused. + */ +-(void) pauseTarget:(id)target; +/** Resumes the target. All queued actions will be resumed. + */ +-(void) resumeTarget:(id)target; + +@end + diff --git a/libs/cocos2d/CCActionManager.m b/libs/cocos2d/CCActionManager.m new file mode 100644 index 0000000..6d307c2 --- /dev/null +++ b/libs/cocos2d/CCActionManager.m @@ -0,0 +1,344 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2009 Valentin Milea + * + * 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 "CCActionManager.h" +#import "CCScheduler.h" +#import "ccMacros.h" + + +// +// singleton stuff +// +static CCActionManager *sharedManager_ = nil; + +@interface CCActionManager (Private) +-(void) removeActionAtIndex:(NSUInteger)index hashElement:(tHashElement*)element; +-(void) deleteHashElement:(tHashElement*)element; +-(void) actionAllocWithHashElement:(tHashElement*)element; +@end + + +@implementation CCActionManager + +#pragma mark ActionManager - init ++ (CCActionManager *)sharedManager +{ + if (!sharedManager_) + sharedManager_ = [[self alloc] init]; + + return sharedManager_; +} + ++(id)alloc +{ + NSAssert(sharedManager_ == nil, @"Attempted to allocate a second instance of a singleton."); + return [super alloc]; +} + ++(void)purgeSharedManager +{ + [[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self]; + [sharedManager_ release]; + sharedManager_ = nil; +} + +-(id) init +{ + if ((self=[super init]) ) { + [[CCScheduler sharedScheduler] scheduleUpdateForTarget:self priority:0 paused:NO]; + targets = NULL; + } + + return self; +} + +- (void) dealloc +{ + CCLOGINFO( @"cocos2d: deallocing %@", self); + + [self removeAllActions]; + + sharedManager_ = nil; + + [super dealloc]; +} + +#pragma mark ActionManager - Private + +-(void) deleteHashElement:(tHashElement*)element +{ + ccArrayFree(element->actions); + HASH_DEL(targets, element); +// CCLOG(@"cocos2d: ---- buckets: %d/%d - %@", targets->entries, targets->size, element->target); + [element->target release]; + free(element); +} + +-(void) actionAllocWithHashElement:(tHashElement*)element +{ + // 4 actions per Node by default + if( element->actions == nil ) + element->actions = ccArrayNew(4); + else if( element->actions->num == element->actions->max ) + ccArrayDoubleCapacity(element->actions); +} + +-(void) removeActionAtIndex:(NSUInteger)index hashElement:(tHashElement*)element +{ + id action = element->actions->arr[index]; + + if( action == element->currentAction && !element->currentActionSalvaged ) { + [element->currentAction retain]; + element->currentActionSalvaged = YES; + } + + ccArrayRemoveObjectAtIndex(element->actions, index); + + // update actionIndex in case we are in tick:, looping over the actions + if( element->actionIndex >= index ) + element->actionIndex--; + + if( element->actions->num == 0 ) { + if( currentTarget == element ) + currentTargetSalvaged = YES; + else + [self deleteHashElement: element]; + } +} + +#pragma mark ActionManager - Pause / Resume + +-(void) pauseTarget:(id)target +{ + tHashElement *element = NULL; + HASH_FIND_INT(targets, &target, element); + if( element ) + element->paused = YES; +// else +// CCLOG(@"cocos2d: pauseAllActions: Target not found"); +} + +-(void) resumeTarget:(id)target +{ + tHashElement *element = NULL; + HASH_FIND_INT(targets, &target, element); + if( element ) + element->paused = NO; +// else +// CCLOG(@"cocos2d: resumeAllActions: Target not found"); +} + +#pragma mark ActionManager - run + +-(void) addAction:(CCAction*)action target:(id)target paused:(BOOL)paused +{ + NSAssert( action != nil, @"Argument action must be non-nil"); + NSAssert( target != nil, @"Argument target must be non-nil"); + + tHashElement *element = NULL; + HASH_FIND_INT(targets, &target, element); + if( ! element ) { + element = calloc( sizeof( *element ), 1 ); + element->paused = paused; + element->target = [target retain]; + HASH_ADD_INT(targets, target, element); +// CCLOG(@"cocos2d: ---- buckets: %d/%d - %@", targets->entries, targets->size, element->target); + + } + + [self actionAllocWithHashElement:element]; + + NSAssert( !ccArrayContainsObject(element->actions, action), @"runAction: Action already running"); + ccArrayAppendObject(element->actions, action); + + [action startWithTarget:target]; +} + +#pragma mark ActionManager - remove + +-(void) removeAllActions +{ + for(tHashElement *element=targets; element != NULL; ) { + id target = element->target; + element = element->hh.next; + [self removeAllActionsFromTarget:target]; + } +} +-(void) removeAllActionsFromTarget:(id)target +{ + // explicit nil handling + if( target == nil ) + return; + + tHashElement *element = NULL; + HASH_FIND_INT(targets, &target, element); + if( element ) { + if( ccArrayContainsObject(element->actions, element->currentAction) && !element->currentActionSalvaged ) { + [element->currentAction retain]; + element->currentActionSalvaged = YES; + } + ccArrayRemoveAllObjects(element->actions); + if( currentTarget == element ) + currentTargetSalvaged = YES; + else + [self deleteHashElement:element]; + } +// else { +// CCLOG(@"cocos2d: removeAllActionsFromTarget: Target not found"); +// } +} + +-(void) removeAction: (CCAction*) action +{ + // explicit nil handling + if (action == nil) + return; + + tHashElement *element = NULL; + id target = [action originalTarget]; + HASH_FIND_INT(targets, &target, element ); + if( element ) { + NSUInteger i = ccArrayGetIndexOfObject(element->actions, action); + if( i != NSNotFound ) + [self removeActionAtIndex:i hashElement:element]; + } +// else { +// CCLOG(@"cocos2d: removeAction: Target not found"); +// } +} + +-(void) removeActionByTag:(NSInteger)aTag target:(id)target +{ + NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag"); + NSAssert( target != nil, @"Target should be ! nil"); + + tHashElement *element = NULL; + HASH_FIND_INT(targets, &target, element); + + if( element ) { + NSUInteger limit = element->actions->num; + for( NSUInteger i = 0; i < limit; i++) { + CCAction *a = element->actions->arr[i]; + + if( a.tag == aTag && [a originalTarget]==target) + return [self removeActionAtIndex:i hashElement:element]; + } +// CCLOG(@"cocos2d: removeActionByTag: Action not found!"); + } +// else { +// CCLOG(@"cocos2d: removeActionByTag: Target not found!"); +// } +} + +#pragma mark ActionManager - get + +-(CCAction*) getActionByTag:(NSInteger)aTag target:(id)target +{ + NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag"); + + tHashElement *element = NULL; + HASH_FIND_INT(targets, &target, element); + + if( element ) { + if( element->actions != nil ) { + NSUInteger limit = element->actions->num; + for( NSUInteger i = 0; i < limit; i++) { + CCAction *a = element->actions->arr[i]; + + if( a.tag == aTag ) + return a; + } + } +// CCLOG(@"cocos2d: getActionByTag: Action not found"); + } +// else { +// CCLOG(@"cocos2d: getActionByTag: Target not found"); +// } + return nil; +} + +-(NSUInteger) numberOfRunningActionsInTarget:(id) target +{ + tHashElement *element = NULL; + HASH_FIND_INT(targets, &target, element); + if( element ) + return element->actions ? element->actions->num : 0; + +// CCLOG(@"cocos2d: numberOfRunningActionsInTarget: Target not found"); + return 0; +} + +#pragma mark ActionManager - main loop + +-(void) update: (ccTime) dt +{ + for(tHashElement *elt = targets; elt != NULL; ) { + + currentTarget = elt; + currentTargetSalvaged = NO; + + if( ! currentTarget->paused ) { + + // The 'actions' ccArray may change while inside this loop. + for( currentTarget->actionIndex = 0; currentTarget->actionIndex < currentTarget->actions->num; currentTarget->actionIndex++) { + currentTarget->currentAction = currentTarget->actions->arr[currentTarget->actionIndex]; + currentTarget->currentActionSalvaged = NO; + + [currentTarget->currentAction step: dt]; + + if( currentTarget->currentActionSalvaged ) { + // The currentAction told the node to remove it. To prevent the action from + // accidentally deallocating itself before finishing its step, we retained + // it. Now that step is done, it's safe to release it. + [currentTarget->currentAction release]; + + } else if( [currentTarget->currentAction isDone] ) { + [currentTarget->currentAction stop]; + + CCAction *a = currentTarget->currentAction; + // Make currentAction nil to prevent removeAction from salvaging it. + currentTarget->currentAction = nil; + [self removeAction:a]; + } + + currentTarget->currentAction = nil; + } + } + + // elt, at this moment, is still valid + // so it is safe to ask this here (issue #490) + elt = elt->hh.next; + + // only delete currentTarget if no actions were scheduled during the cycle (issue #481) + if( currentTargetSalvaged && currentTarget->actions->num == 0 ) + [self deleteHashElement:currentTarget]; + } + + // issue #635 + currentTarget = nil; +} +@end diff --git a/libs/cocos2d/CCActionPageTurn3D.h b/libs/cocos2d/CCActionPageTurn3D.h new file mode 100644 index 0000000..39eb31d --- /dev/null +++ b/libs/cocos2d/CCActionPageTurn3D.h @@ -0,0 +1,42 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Sindesso Pty Ltd http://www.sindesso.com/ + * + * 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 "CCActionGrid3D.h" + +/** + * This action simulates a page turn from the bottom right hand corner of the screen + * It's not much use by itself but is used by the PageTurnTransition. + * + * Based on an original paper by L Hong et al. + * http://www.parc.com/publication/1638/turning-pages-of-3d-electronic-books.html + * + * @since v0.8.2 + */ +@interface CCPageTurn3D : CCGrid3DAction +{ +} + +@end diff --git a/libs/cocos2d/CCActionPageTurn3D.m b/libs/cocos2d/CCActionPageTurn3D.m new file mode 100644 index 0000000..ee59500 --- /dev/null +++ b/libs/cocos2d/CCActionPageTurn3D.m @@ -0,0 +1,86 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Sindesso Pty Ltd http://www.sindesso.com/ + * + * 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 "CCActionPageTurn3D.h" + +@implementation CCPageTurn3D + +/* + * Update each tick + * Time is the percentage of the way through the duration + */ +-(void)update:(ccTime)time +{ + float tt = MAX( 0, time - 0.25f ); + float deltaAy = ( tt * tt * 500); + float ay = -100 - deltaAy; + + float deltaTheta = - (float) M_PI_2 * sqrtf( time) ; + float theta = /*0.01f*/ + (float) M_PI_2 +deltaTheta; + + float sinTheta = sinf(theta); + float cosTheta = cosf(theta); + + for( int i = 0; i <=gridSize_.x; i++ ) + { + for( int j = 0; j <= gridSize_.y; j++ ) + { + // Get original vertex + ccVertex3F p = [self originalVertex:ccg(i,j)]; + + float R = sqrtf(p.x*p.x + (p.y - ay) * (p.y - ay)); + float r = R * sinTheta; + float alpha = asinf( p.x / R ); + float beta = alpha / sinTheta; + float cosBeta = cosf( beta ); + + // If beta > PI then we've wrapped around the cone + // Reduce the radius to stop these points interfering with others + if( beta <= M_PI) + p.x = ( r * sinf(beta)); + else + { + // Force X = 0 to stop wrapped + // points + p.x = 0; + } + + p.y = ( R + ay - ( r*(1 - cosBeta)*sinTheta)); + + // We scale z here to avoid the animation being + // too much bigger than the screen due to perspectve transform + p.z = (r * ( 1 - cosBeta ) * cosTheta) / 7; // "100" didn't work for + + // Stop z coord from dropping beneath underlying page in a transition + // issue #751 + if( p.z<0.5f ) + p.z = 0.5f; + + // Set new coords + [self setVertex:ccg(i,j) vertex:p]; + } + } +} +@end diff --git a/libs/cocos2d/CCActionProgressTimer.h b/libs/cocos2d/CCActionProgressTimer.h new file mode 100644 index 0000000..500631b --- /dev/null +++ b/libs/cocos2d/CCActionProgressTimer.h @@ -0,0 +1,59 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (C) 2010 Lam Pham + * + * 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 +#import "CCProgressTimer.h" +#import "CCActionInterval.h" + +/** + Progress to percentage +@since v0.99.1 +*/ +@interface CCProgressTo : CCActionInterval +{ + float to_; + float from_; +} +/** Creates and initializes with a duration and a percent */ ++(id) actionWithDuration:(ccTime)duration percent:(float)percent; +/** Initializes with a duration and a percent */ +-(id) initWithDuration:(ccTime)duration percent:(float)percent; +@end + +/** + Progress from a percentage to another percentage + @since v0.99.1 + */ +@interface CCProgressFromTo : CCActionInterval +{ + float to_; + float from_; +} +/** Creates and initializes the action with a duration, a "from" percentage and a "to" percentage */ ++(id) actionWithDuration:(ccTime)duration from:(float)fromPercentage to:(float) toPercentage; +/** Initializes the action with a duration, a "from" percentage and a "to" percentage */ +-(id) initWithDuration:(ccTime)duration from:(float)fromPercentage to:(float) toPercentage; +@end diff --git a/libs/cocos2d/CCActionProgressTimer.m b/libs/cocos2d/CCActionProgressTimer.m new file mode 100644 index 0000000..c242570 --- /dev/null +++ b/libs/cocos2d/CCActionProgressTimer.m @@ -0,0 +1,103 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (C) 2010 Lam Pham + * + * 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 "CCActionProgressTimer.h" + +#define kProgressTimerCast CCProgressTimer* + +@implementation CCProgressTo ++(id) actionWithDuration: (ccTime) t percent: (float) v +{ + return [[[ self alloc] initWithDuration: t percent: v] autorelease]; +} + +-(id) initWithDuration: (ccTime) t percent: (float) v +{ + if( (self=[super initWithDuration: t] ) ) + to_ = v; + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:duration_ percent:to_]; + return copy; +} + +-(void) startWithTarget:(id) aTarget; +{ + [super startWithTarget:aTarget]; + from_ = [(kProgressTimerCast)target_ percentage]; + + // XXX: Is this correct ? + // Adding it to support CCRepeat + if( from_ == 100) + from_ = 0; +} + +-(void) update: (ccTime) t +{ + [(kProgressTimerCast)target_ setPercentage: from_ + ( to_ - from_ ) * t]; +} +@end + +@implementation CCProgressFromTo ++(id) actionWithDuration: (ccTime) t from:(float)fromPercentage to:(float) toPercentage +{ + return [[[self alloc] initWithDuration: t from: fromPercentage to: toPercentage] autorelease]; +} + +-(id) initWithDuration: (ccTime) t from:(float)fromPercentage to:(float) toPercentage +{ + if( (self=[super initWithDuration: t] ) ){ + to_ = toPercentage; + from_ = fromPercentage; + } + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:duration_ from:from_ to:to_]; + return copy; +} + +- (CCActionInterval *) reverse +{ + return [[self class] actionWithDuration:duration_ from:to_ to:from_]; +} + +-(void) startWithTarget:(id) aTarget; +{ + [super startWithTarget:aTarget]; +} + +-(void) update: (ccTime) t +{ + [(kProgressTimerCast)target_ setPercentage: from_ + ( to_ - from_ ) * t]; +} +@end diff --git a/libs/cocos2d/CCActionTiledGrid.h b/libs/cocos2d/CCActionTiledGrid.h new file mode 100644 index 0000000..d66132d --- /dev/null +++ b/libs/cocos2d/CCActionTiledGrid.h @@ -0,0 +1,211 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 On-Core + * + * 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 "CCActionGrid.h" + +/** CCShakyTiles3D action */ +@interface CCShakyTiles3D : CCTiledGrid3DAction +{ + int randrange; + BOOL shakeZ; +} + +/** creates the action with a range, whether or not to shake Z vertices, a grid size, and duration */ ++(id)actionWithRange:(int)range shakeZ:(BOOL)shakeZ grid:(ccGridSize)gridSize duration:(ccTime)d; +/** initializes the action with a range, whether or not to shake Z vertices, a grid size, and duration */ +-(id)initWithRange:(int)range shakeZ:(BOOL)shakeZ grid:(ccGridSize)gridSize duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** CCShatteredTiles3D action */ +@interface CCShatteredTiles3D : CCTiledGrid3DAction +{ + int randrange; + BOOL once; + BOOL shatterZ; +} + +/** creates the action with a range, whether of not to shatter Z vertices, a grid size and duration */ ++(id)actionWithRange:(int)range shatterZ:(BOOL)shatterZ grid:(ccGridSize)gridSize duration:(ccTime)d; +/** initializes the action with a range, whether or not to shatter Z vertices, a grid size and duration */ +-(id)initWithRange:(int)range shatterZ:(BOOL)shatterZ grid:(ccGridSize)gridSize duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** CCShuffleTiles action + Shuffle the tiles in random order + */ +@interface CCShuffleTiles : CCTiledGrid3DAction +{ + int seed; + NSUInteger tilesCount; + int *tilesOrder; + void *tiles; +} + +/** creates the action with a random seed, the grid size and the duration */ ++(id)actionWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d; +/** initializes the action with a random seed, the grid size and the duration */ +-(id)initWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** CCFadeOutTRTiles action + Fades out the tiles in a Top-Right direction + */ +@interface CCFadeOutTRTiles : CCTiledGrid3DAction +{ +} +@end + +//////////////////////////////////////////////////////////// + +/** CCFadeOutBLTiles action. + Fades out the tiles in a Bottom-Left direction + */ +@interface CCFadeOutBLTiles : CCFadeOutTRTiles +{ +} +@end + +//////////////////////////////////////////////////////////// + +/** CCFadeOutUpTiles action. + Fades out the tiles in upwards direction + */ +@interface CCFadeOutUpTiles : CCFadeOutTRTiles +{ +} +@end + +//////////////////////////////////////////////////////////// + +/** CCFadeOutDownTiles action. + Fades out the tiles in downwards direction + */ +@interface CCFadeOutDownTiles : CCFadeOutUpTiles +{ +} +@end + +//////////////////////////////////////////////////////////// + +/** CCTurnOffTiles action. + Turn off the files in random order + */ +@interface CCTurnOffTiles : CCTiledGrid3DAction +{ + int seed; + NSUInteger tilesCount; + int *tilesOrder; +} + +/** creates the action with a random seed, the grid size and the duration */ ++(id)actionWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d; +/** initializes the action with a random seed, the grid size and the duration */ +-(id)initWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d; +@end + +//////////////////////////////////////////////////////////// + +/** CCWavesTiles3D action. */ +@interface CCWavesTiles3D : CCTiledGrid3DAction +{ + int waves; + float amplitude; + float amplitudeRate; +} + +/** waves amplitude */ +@property (nonatomic,readwrite) float amplitude; +/** waves amplitude rate */ +@property (nonatomic,readwrite) float amplitudeRate; + +/** creates the action with a number of waves, the waves amplitude, the grid size and the duration */ ++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d; +/** initializes the action with a number of waves, the waves amplitude, the grid size and the duration */ +-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** CCJumpTiles3D action. + A sin function is executed to move the tiles across the Z axis + */ +@interface CCJumpTiles3D : CCTiledGrid3DAction +{ + int jumps; + float amplitude; + float amplitudeRate; +} + +/** amplitude of the sin*/ +@property (nonatomic,readwrite) float amplitude; +/** amplitude rate */ +@property (nonatomic,readwrite) float amplitudeRate; + +/** creates the action with the number of jumps, the sin amplitude, the grid size and the duration */ ++(id)actionWithJumps:(int)j amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d; +/** initializes the action with the number of jumps, the sin amplitude, the grid size and the duration */ +-(id)initWithJumps:(int)j amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** CCSplitRows action */ +@interface CCSplitRows : CCTiledGrid3DAction +{ + int rows; + CGSize winSize; +} +/** creates the action with the number of rows to split and the duration */ ++(id)actionWithRows:(int)rows duration:(ccTime)duration; +/** initializes the action with the number of rows to split and the duration */ +-(id)initWithRows:(int)rows duration:(ccTime)duration; + +@end + +//////////////////////////////////////////////////////////// + +/** CCSplitCols action */ +@interface CCSplitCols : CCTiledGrid3DAction +{ + int cols; + CGSize winSize; +} +/** creates the action with the number of columns to split and the duration */ ++(id)actionWithCols:(int)cols duration:(ccTime)duration; +/** initializes the action with the number of columns to split and the duration */ +-(id)initWithCols:(int)cols duration:(ccTime)duration; + +@end diff --git a/libs/cocos2d/CCActionTiledGrid.m b/libs/cocos2d/CCActionTiledGrid.m new file mode 100644 index 0000000..75965ec --- /dev/null +++ b/libs/cocos2d/CCActionTiledGrid.m @@ -0,0 +1,768 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 On-Core + * + * 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 "CCActionTiledGrid.h" +#import "CCDirector.h" +#import "ccMacros.h" +#import "Support/CGPointExtension.h" + +typedef struct +{ + CGPoint position; + CGPoint startPosition; + ccGridSize delta; +} Tile; + +#pragma mark - +#pragma mark ShakyTiles3D + +@implementation CCShakyTiles3D + ++(id)actionWithRange:(int)range shakeZ:(BOOL)shakeZ grid:(ccGridSize)gridSize duration:(ccTime)d +{ + return [[[self alloc] initWithRange:range shakeZ:shakeZ grid:gridSize duration:d] autorelease]; +} + +-(id)initWithRange:(int)range shakeZ:(BOOL)sz grid:(ccGridSize)gSize duration:(ccTime)d +{ + if ( (self = [super initWithSize:gSize duration:d]) ) + { + randrange = range; + shakeZ = sz; + } + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithRange:randrange shakeZ:shakeZ grid:gridSize_ duration:duration_]; + return copy; +} + + +-(void)update:(ccTime)time +{ + int i, j; + + for( i = 0; i < gridSize_.x; i++ ) + { + for( j = 0; j < gridSize_.y; j++ ) + { + ccQuad3 coords = [self originalTile:ccg(i,j)]; + + // X + coords.bl.x += ( rand() % (randrange*2) ) - randrange; + coords.br.x += ( rand() % (randrange*2) ) - randrange; + coords.tl.x += ( rand() % (randrange*2) ) - randrange; + coords.tr.x += ( rand() % (randrange*2) ) - randrange; + + // Y + coords.bl.y += ( rand() % (randrange*2) ) - randrange; + coords.br.y += ( rand() % (randrange*2) ) - randrange; + coords.tl.y += ( rand() % (randrange*2) ) - randrange; + coords.tr.y += ( rand() % (randrange*2) ) - randrange; + + if( shakeZ ) { + coords.bl.z += ( rand() % (randrange*2) ) - randrange; + coords.br.z += ( rand() % (randrange*2) ) - randrange; + coords.tl.z += ( rand() % (randrange*2) ) - randrange; + coords.tr.z += ( rand() % (randrange*2) ) - randrange; + } + + [self setTile:ccg(i,j) coords:coords]; + } + } +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark CCShatteredTiles3D + +@implementation CCShatteredTiles3D + ++(id)actionWithRange:(int)range shatterZ:(BOOL)sz grid:(ccGridSize)gridSize duration:(ccTime)d +{ + return [[[self alloc] initWithRange:range shatterZ:sz grid:gridSize duration:d] autorelease]; +} + +-(id)initWithRange:(int)range shatterZ:(BOOL)sz grid:(ccGridSize)gSize duration:(ccTime)d +{ + if ( (self = [super initWithSize:gSize duration:d]) ) + { + once = NO; + randrange = range; + shatterZ = sz; + } + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithRange:randrange shatterZ:shatterZ grid:gridSize_ duration:duration_]; + return copy; +} + + +-(void)update:(ccTime)time +{ + int i, j; + + if ( once == NO ) + { + for( i = 0; i < gridSize_.x; i++ ) + { + for( j = 0; j < gridSize_.y; j++ ) + { + ccQuad3 coords = [self originalTile:ccg(i,j)]; + + // X + coords.bl.x += ( rand() % (randrange*2) ) - randrange; + coords.br.x += ( rand() % (randrange*2) ) - randrange; + coords.tl.x += ( rand() % (randrange*2) ) - randrange; + coords.tr.x += ( rand() % (randrange*2) ) - randrange; + + // Y + coords.bl.y += ( rand() % (randrange*2) ) - randrange; + coords.br.y += ( rand() % (randrange*2) ) - randrange; + coords.tl.y += ( rand() % (randrange*2) ) - randrange; + coords.tr.y += ( rand() % (randrange*2) ) - randrange; + + if( shatterZ ) { + coords.bl.z += ( rand() % (randrange*2) ) - randrange; + coords.br.z += ( rand() % (randrange*2) ) - randrange; + coords.tl.z += ( rand() % (randrange*2) ) - randrange; + coords.tr.z += ( rand() % (randrange*2) ) - randrange; + } + + [self setTile:ccg(i,j) coords:coords]; + } + } + + once = YES; + } +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark CCShuffleTiles + +@implementation CCShuffleTiles + ++(id)actionWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d +{ + return [[[self alloc] initWithSeed:s grid:gridSize duration:d] autorelease]; +} + +-(id)initWithSeed:(int)s grid:(ccGridSize)gSize duration:(ccTime)d +{ + if ( (self = [super initWithSize:gSize duration:d]) ) + { + seed = s; + tilesOrder = nil; + tiles = nil; + } + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithSeed:seed grid:gridSize_ duration:duration_]; + return copy; +} + + +-(void)dealloc +{ + if ( tilesOrder ) free(tilesOrder); + if ( tiles ) free(tiles); + [super dealloc]; +} + +-(void)shuffle:(int*)array count:(NSUInteger)len +{ + NSInteger i; + for( i = len - 1; i >= 0; i-- ) + { + NSInteger j = rand() % (i+1); + int v = array[i]; + array[i] = array[j]; + array[j] = v; + } +} + +-(ccGridSize)getDelta:(ccGridSize)pos +{ + CGPoint pos2; + + NSInteger idx = pos.x * gridSize_.y + pos.y; + + pos2.x = tilesOrder[idx] / (int)gridSize_.y; + pos2.y = tilesOrder[idx] % (int)gridSize_.y; + + return ccg(pos2.x - pos.x, pos2.y - pos.y); +} + +-(void)placeTile:(ccGridSize)pos tile:(Tile)t +{ + ccQuad3 coords = [self originalTile:pos]; + + CGPoint step = [[target_ grid] step]; + coords.bl.x += (int)(t.position.x * step.x); + coords.bl.y += (int)(t.position.y * step.y); + + coords.br.x += (int)(t.position.x * step.x); + coords.br.y += (int)(t.position.y * step.y); + + coords.tl.x += (int)(t.position.x * step.x); + coords.tl.y += (int)(t.position.y * step.y); + + coords.tr.x += (int)(t.position.x * step.x); + coords.tr.y += (int)(t.position.y * step.y); + + [self setTile:pos coords:coords]; +} + +-(void)startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + + if ( seed != -1 ) + srand(seed); + + tilesCount = gridSize_.x * gridSize_.y; + tilesOrder = (int*)malloc(tilesCount*sizeof(int)); + int i, j; + + for( i = 0; i < tilesCount; i++ ) + tilesOrder[i] = i; + + [self shuffle:tilesOrder count:tilesCount]; + + tiles = malloc(tilesCount*sizeof(Tile)); + Tile *tileArray = (Tile*)tiles; + + for( i = 0; i < gridSize_.x; i++ ) + { + for( j = 0; j < gridSize_.y; j++ ) + { + tileArray->position = ccp(i,j); + tileArray->startPosition = ccp(i,j); + tileArray->delta = [self getDelta:ccg(i,j)]; + tileArray++; + } + } +} + +-(void)update:(ccTime)time +{ + int i, j; + + Tile *tileArray = (Tile*)tiles; + + for( i = 0; i < gridSize_.x; i++ ) + { + for( j = 0; j < gridSize_.y; j++ ) + { + tileArray->position = ccpMult( ccp(tileArray->delta.x, tileArray->delta.y), time); + [self placeTile:ccg(i,j) tile:*tileArray]; + tileArray++; + } + } +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark CCFadeOutTRTiles + +@implementation CCFadeOutTRTiles + +-(float)testFunc:(ccGridSize)pos time:(ccTime)time +{ + CGPoint n = ccpMult( ccp(gridSize_.x,gridSize_.y), time); + if ( (n.x+n.y) == 0.0f ) + return 1.0f; + + return powf( (pos.x+pos.y) / (n.x+n.y), 6 ); +} + +-(void)turnOnTile:(ccGridSize)pos +{ + [self setTile:pos coords:[self originalTile:pos]]; +} + +-(void)turnOffTile:(ccGridSize)pos +{ + ccQuad3 coords; + bzero(&coords, sizeof(ccQuad3)); + [self setTile:pos coords:coords]; +} + +-(void)transformTile:(ccGridSize)pos distance:(float)distance +{ + ccQuad3 coords = [self originalTile:pos]; + CGPoint step = [[target_ grid] step]; + + coords.bl.x += (step.x / 2) * (1.0f - distance); + coords.bl.y += (step.y / 2) * (1.0f - distance); + + coords.br.x -= (step.x / 2) * (1.0f - distance); + coords.br.y += (step.y / 2) * (1.0f - distance); + + coords.tl.x += (step.x / 2) * (1.0f - distance); + coords.tl.y -= (step.y / 2) * (1.0f - distance); + + coords.tr.x -= (step.x / 2) * (1.0f - distance); + coords.tr.y -= (step.y / 2) * (1.0f - distance); + + [self setTile:pos coords:coords]; +} + +-(void)update:(ccTime)time +{ + int i, j; + + for( i = 0; i < gridSize_.x; i++ ) + { + for( j = 0; j < gridSize_.y; j++ ) + { + float distance = [self testFunc:ccg(i,j) time:time]; + if ( distance == 0 ) + [self turnOffTile:ccg(i,j)]; + else if ( distance < 1 ) + [self transformTile:ccg(i,j) distance:distance]; + else + [self turnOnTile:ccg(i,j)]; + } + } +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark CCFadeOutBLTiles + +@implementation CCFadeOutBLTiles + +-(float)testFunc:(ccGridSize)pos time:(ccTime)time +{ + CGPoint n = ccpMult(ccp(gridSize_.x, gridSize_.y), (1.0f-time)); + if ( (pos.x+pos.y) == 0 ) + return 1.0f; + + return powf( (n.x+n.y) / (pos.x+pos.y), 6 ); +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark CCFadeOutUpTiles + +@implementation CCFadeOutUpTiles + +-(float)testFunc:(ccGridSize)pos time:(ccTime)time +{ + CGPoint n = ccpMult(ccp(gridSize_.x, gridSize_.y), time); + if ( n.y == 0 ) + return 1.0f; + + return powf( pos.y / n.y, 6 ); +} + +-(void)transformTile:(ccGridSize)pos distance:(float)distance +{ + ccQuad3 coords = [self originalTile:pos]; + CGPoint step = [[target_ grid] step]; + + coords.bl.y += (step.y / 2) * (1.0f - distance); + coords.br.y += (step.y / 2) * (1.0f - distance); + coords.tl.y -= (step.y / 2) * (1.0f - distance); + coords.tr.y -= (step.y / 2) * (1.0f - distance); + + [self setTile:pos coords:coords]; +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark CCFadeOutDownTiles + +@implementation CCFadeOutDownTiles + +-(float)testFunc:(ccGridSize)pos time:(ccTime)time +{ + CGPoint n = ccpMult(ccp(gridSize_.x,gridSize_.y), (1.0f - time)); + if ( pos.y == 0 ) + return 1.0f; + + return powf( n.y / pos.y, 6 ); +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark TurnOffTiles + +@implementation CCTurnOffTiles + ++(id)actionWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d +{ + return [[[self alloc] initWithSeed:s grid:gridSize duration:d] autorelease]; +} + +-(id)initWithSeed:(int)s grid:(ccGridSize)gSize duration:(ccTime)d +{ + if ( (self = [super initWithSize:gSize duration:d]) ) + { + seed = s; + tilesOrder = nil; + } + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithSeed:seed grid:gridSize_ duration:duration_]; + return copy; +} + +-(void)dealloc +{ + if ( tilesOrder ) free(tilesOrder); + [super dealloc]; +} + +-(void)shuffle:(int*)array count:(NSUInteger)len +{ + NSInteger i; + for( i = len - 1; i >= 0; i-- ) + { + NSInteger j = rand() % (i+1); + int v = array[i]; + array[i] = array[j]; + array[j] = v; + } +} + +-(void)turnOnTile:(ccGridSize)pos +{ + [self setTile:pos coords:[self originalTile:pos]]; +} + +-(void)turnOffTile:(ccGridSize)pos +{ + ccQuad3 coords; + + bzero(&coords, sizeof(ccQuad3)); + [self setTile:pos coords:coords]; +} + +-(void)startWithTarget:(id)aTarget +{ + int i; + + [super startWithTarget:aTarget]; + + if ( seed != -1 ) + srand(seed); + + tilesCount = gridSize_.x * gridSize_.y; + tilesOrder = (int*)malloc(tilesCount*sizeof(int)); + + for( i = 0; i < tilesCount; i++ ) + tilesOrder[i] = i; + + [self shuffle:tilesOrder count:tilesCount]; +} + +-(void)update:(ccTime)time +{ + int i, l, t; + + l = (int)(time * (float)tilesCount); + + for( i = 0; i < tilesCount; i++ ) + { + t = tilesOrder[i]; + ccGridSize tilePos = ccg( t / gridSize_.y, t % gridSize_.y ); + + if ( i < l ) + [self turnOffTile:tilePos]; + else + [self turnOnTile:tilePos]; + } +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark CCWavesTiles3D + +@implementation CCWavesTiles3D + +@synthesize amplitude; +@synthesize amplitudeRate; + ++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d +{ + return [[[self alloc] initWithWaves:wav amplitude:amp grid:gridSize duration:d] autorelease]; +} + +-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d +{ + if ( (self = [super initWithSize:gSize duration:d]) ) + { + waves = wav; + amplitude = amp; + amplitudeRate = 1.0f; + } + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithWaves:waves amplitude:amplitude grid:gridSize_ duration:duration_]; + return copy; +} + + +-(void)update:(ccTime)time +{ + int i, j; + + for( i = 0; i < gridSize_.x; i++ ) + { + for( j = 0; j < gridSize_.y; j++ ) + { + ccQuad3 coords = [self originalTile:ccg(i,j)]; + + coords.bl.z = (sinf(time*(CGFloat)M_PI*waves*2 + (coords.bl.y+coords.bl.x) * .01f) * amplitude * amplitudeRate ); + coords.br.z = coords.bl.z; + coords.tl.z = coords.bl.z; + coords.tr.z = coords.bl.z; + + [self setTile:ccg(i,j) coords:coords]; + } + } +} +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark CCJumpTiles3D + +@implementation CCJumpTiles3D + +@synthesize amplitude; +@synthesize amplitudeRate; + ++(id)actionWithJumps:(int)j amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d +{ + return [[[self alloc] initWithJumps:j amplitude:amp grid:gridSize duration:d] autorelease]; +} + +-(id)initWithJumps:(int)j amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d +{ + if ( (self = [super initWithSize:gSize duration:d]) ) + { + jumps = j; + amplitude = amp; + amplitudeRate = 1.0f; + } + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithJumps:jumps amplitude:amplitude grid:gridSize_ duration:duration_]; + return copy; +} + + +-(void)update:(ccTime)time +{ + int i, j; + + float sinz = (sinf((CGFloat)M_PI*time*jumps*2) * amplitude * amplitudeRate ); + float sinz2 = (sinf((CGFloat)M_PI*(time*jumps*2 + 1)) * amplitude * amplitudeRate ); + + for( i = 0; i < gridSize_.x; i++ ) + { + for( j = 0; j < gridSize_.y; j++ ) + { + ccQuad3 coords = [self originalTile:ccg(i,j)]; + + if ( ((i+j) % 2) == 0 ) + { + coords.bl.z += sinz; + coords.br.z += sinz; + coords.tl.z += sinz; + coords.tr.z += sinz; + } + else + { + coords.bl.z += sinz2; + coords.br.z += sinz2; + coords.tl.z += sinz2; + coords.tr.z += sinz2; + } + + [self setTile:ccg(i,j) coords:coords]; + } + } +} +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark SplitRows + +@implementation CCSplitRows + ++(id)actionWithRows:(int)r duration:(ccTime)d +{ + return [[[self alloc] initWithRows:r duration:d] autorelease]; +} + +-(id)initWithRows:(int)r duration:(ccTime)d +{ + rows = r; + return [super initWithSize:ccg(1,r) duration:d]; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithRows:rows duration:duration_]; + return copy; +} + +-(void)startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + winSize = [[CCDirector sharedDirector] winSizeInPixels]; +} + +-(void)update:(ccTime)time +{ + int j; + + for( j = 0; j < gridSize_.y; j++ ) + { + ccQuad3 coords = [self originalTile:ccg(0,j)]; + float direction = 1; + + if ( (j % 2 ) == 0 ) + direction = -1; + + coords.bl.x += direction * winSize.width * time; + coords.br.x += direction * winSize.width * time; + coords.tl.x += direction * winSize.width * time; + coords.tr.x += direction * winSize.width * time; + + [self setTile:ccg(0,j) coords:coords]; + } +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark CCSplitCols + +@implementation CCSplitCols + ++(id)actionWithCols:(int)c duration:(ccTime)d +{ + return [[[self alloc] initWithCols:c duration:d] autorelease]; +} + +-(id)initWithCols:(int)c duration:(ccTime)d +{ + cols = c; + return [super initWithSize:ccg(c,1) duration:d]; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCGridAction *copy = [[[self class] allocWithZone:zone] initWithCols:cols duration:duration_]; + return copy; +} + +-(void)startWithTarget:(id)aTarget +{ + [super startWithTarget:aTarget]; + winSize = [[CCDirector sharedDirector] winSizeInPixels]; +} + +-(void)update:(ccTime)time +{ + int i; + + for( i = 0; i < gridSize_.x; i++ ) + { + ccQuad3 coords = [self originalTile:ccg(i,0)]; + float direction = 1; + + if ( (i % 2 ) == 0 ) + direction = -1; + + coords.bl.y += direction * winSize.height * time; + coords.br.y += direction * winSize.height * time; + coords.tl.y += direction * winSize.height * time; + coords.tr.y += direction * winSize.height * time; + + [self setTile:ccg(i,0) coords:coords]; + } +} + +@end diff --git a/libs/cocos2d/CCActionTween.h b/libs/cocos2d/CCActionTween.h new file mode 100644 index 0000000..69fdea5 --- /dev/null +++ b/libs/cocos2d/CCActionTween.h @@ -0,0 +1,62 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright 2009 lhunath (Maarten Billemont) + * + * 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 +#import "CCActionInterval.h" + +/** CCActionTween + + CCActionTween is an action that lets you update any property of an object. + For example, if you want to modify the "width" property of a target from 200 to 300 in 2 senconds, then: + + id modifyWidth = [CCActionTween actionWithDuration:2 key:@"width" from:200 to:300]; + [target runAction:modifyWidth]; + + + Another example: CCScaleTo action could be rewriten using CCPropertyAction: + + // scaleA and scaleB are equivalents + id scaleA = [CCScaleTo actionWithDuration:2 scale:3]; + id scaleB = [CCActionTween actionWithDuration:2 key:@"scale" from:1 to:3]; + + + @since v0.99.2 + */ +@interface CCActionTween : CCActionInterval +{ + NSString *key_; + + float from_, to_; + float delta_; +} + +/** creates an initializes the action with the property name (key), and the from and to parameters. */ ++ (id)actionWithDuration:(ccTime)aDuration key:(NSString *)key from:(float)from to:(float)to; + +/** initializes the action with the property name (key), and the from and to parameters. */ +- (id)initWithDuration:(ccTime)aDuration key:(NSString *)key from:(float)from to:(float)to; + +@end diff --git a/libs/cocos2d/CCActionTween.m b/libs/cocos2d/CCActionTween.m new file mode 100644 index 0000000..95ae572 --- /dev/null +++ b/libs/cocos2d/CCActionTween.m @@ -0,0 +1,72 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright 2009 lhunath (Maarten Billemont) + * + * 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 "CCActionTween.h" + + +@implementation CCActionTween + ++ (id)actionWithDuration:(ccTime)aDuration key:(NSString *)aKey from:(float)aFrom to:(float)aTo { + + return [[[[self class] alloc] initWithDuration:aDuration key:aKey from:aFrom to:aTo] autorelease]; +} + +- (id)initWithDuration:(ccTime)aDuration key:(NSString *)key from:(float)from to:(float)to { + + if ((self = [super initWithDuration:aDuration])) { + + key_ = [key copy]; + to_ = to; + from_ = from; + + } + + return self; +} + +- (void) dealloc +{ + [key_ release]; + [super dealloc]; +} + +- (void)startWithTarget:aTarget +{ + [super startWithTarget:aTarget]; + delta_ = to_ - from_; +} + +- (void) update:(ccTime) dt +{ + [target_ setValue:[NSNumber numberWithFloat:to_ - delta_ * (1 - dt)] forKey:key_]; +} + +- (CCActionInterval *) reverse +{ + return [[self class] actionWithDuration:duration_ key:key_ from:to_ to:from_]; +} + + +@end diff --git a/libs/cocos2d/CCAnimation.h b/libs/cocos2d/CCAnimation.h new file mode 100644 index 0000000..1ccfcce --- /dev/null +++ b/libs/cocos2d/CCAnimation.h @@ -0,0 +1,135 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import +#endif // IPHONE + +@class CCSpriteFrame; +@class CCTexture2D; + +/** A CCAnimation object is used to perform animations on the CCSprite objects. + + The CCAnimation object contains CCSpriteFrame objects, and a possible delay between the frames. + You can animate a CCAnimation object by using the CCAnimate action. Example: + + [sprite runAction:[CCAnimate actionWithAnimation:animation]]; + + */ +@interface CCAnimation : NSObject +{ + NSString *name_; + float delay_; + NSMutableArray *frames_; +} + +/** name of the animation */ +@property (nonatomic,readwrite,retain) NSString *name; +/** delay between frames in seconds. */ +@property (nonatomic,readwrite,assign) float delay; +/** array of frames */ +@property (nonatomic,readwrite,retain) NSMutableArray *frames; + +/** Creates an animation + @since v0.99.5 + */ ++(id) animation; + +/** Creates an animation with frames. + @since v0.99.5 + */ ++(id) animationWithFrames:(NSArray*)frames; + +/* Creates an animation with frames and a delay between frames. + @since v0.99.5 + */ ++(id) animationWithFrames:(NSArray*)frames delay:(float)delay; + +/** Creates a CCAnimation with a name + @since v0.99.3 + @deprecated Will be removed in 1.0.1. Use "animation" instead. + */ ++(id) animationWithName:(NSString*)name DEPRECATED_ATTRIBUTE; + +/** Creates a CCAnimation with a name and frames + @since v0.99.3 + @deprecated Will be removed in 1.0.1. Use "animationWithFrames" instead. + */ ++(id) animationWithName:(NSString*)name frames:(NSArray*)frames DEPRECATED_ATTRIBUTE; + +/** Creates a CCAnimation with a name and delay between frames. */ ++(id) animationWithName:(NSString*)name delay:(float)delay DEPRECATED_ATTRIBUTE; + +/** Creates a CCAnimation with a name, delay and an array of CCSpriteFrames. */ ++(id) animationWithName:(NSString*)name delay:(float)delay frames:(NSArray*)frames DEPRECATED_ATTRIBUTE; + + +/** Initializes a CCAnimation with frames. + @since v0.99.5 +*/ +-(id) initWithFrames:(NSArray*)frames; + +/** Initializes a CCAnimation with frames and a delay between frames + @since v0.99.5 + */ +-(id) initWithFrames:(NSArray *)frames delay:(float)delay; + +/** Initializes a CCAnimation with a name + @since v0.99.3 + @deprecated Will be removed in 1.0.1. Use "init" instead. + */ +-(id) initWithName:(NSString*)name DEPRECATED_ATTRIBUTE; + +/** Initializes a CCAnimation with a name and frames + @since v0.99.3 + @deprecated Will be removed in 1.0.1. Use "initWithFrames" instead. + */ +-(id) initWithName:(NSString*)name frames:(NSArray*)frames DEPRECATED_ATTRIBUTE; + +/** Initializes a CCAnimation with a name and delay between frames. + @deprecated Will be removed in 1.0.1. Use "initWithFrames:nil delay:delay" instead. +*/ +-(id) initWithName:(NSString*)name delay:(float)delay DEPRECATED_ATTRIBUTE; + +/** Initializes a CCAnimation with a name, delay and an array of CCSpriteFrames. + @deprecated Will be removed in 1.0.1. Use "initWithFrames:frames delay:delay" instead. +*/ +-(id) initWithName:(NSString*)name delay:(float)delay frames:(NSArray*)frames DEPRECATED_ATTRIBUTE; + +/** Adds a frame to a CCAnimation. */ +-(void) addFrame:(CCSpriteFrame*)frame; + +/** Adds a frame with an image filename. Internally it will create a CCSpriteFrame and it will add it. + Added to facilitate the migration from v0.8 to v0.9. + */ +-(void) addFrameWithFilename:(NSString*)filename; + +/** Adds a frame with a texture and a rect. Internally it will create a CCSpriteFrame and it will add it. + Added to facilitate the migration from v0.8 to v0.9. + */ +-(void) addFrameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect; + +@end diff --git a/libs/cocos2d/CCAnimation.m b/libs/cocos2d/CCAnimation.m new file mode 100644 index 0000000..bb8480c --- /dev/null +++ b/libs/cocos2d/CCAnimation.m @@ -0,0 +1,152 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "ccMacros.h" +#import "CCAnimation.h" +#import "CCSpriteFrame.h" +#import "CCTexture2D.h" +#import "CCTextureCache.h" + +@implementation CCAnimation +@synthesize name = name_, delay = delay_, frames = frames_; + ++(id) animation +{ + return [[[self alloc] init] autorelease]; +} + ++(id) animationWithFrames:(NSArray*)frames +{ + return [[[self alloc] initWithFrames:frames] autorelease]; +} + ++(id) animationWithFrames:(NSArray*)frames delay:(float)delay +{ + return [[[self alloc] initWithFrames:frames delay:delay] autorelease]; +} + ++(id) animationWithName:(NSString*)name +{ + return [[[self alloc] initWithName:name] autorelease]; +} + ++(id) animationWithName:(NSString*)name frames:(NSArray*)frames +{ + return [[[self alloc] initWithName:name frames:frames] autorelease]; +} + ++(id) animationWithName:(NSString*)aname delay:(float)d frames:(NSArray*)array +{ + return [[[self alloc] initWithName:aname delay:d frames:array] autorelease]; +} + ++(id) animationWithName:(NSString*)aname delay:(float)d +{ + return [[[self alloc] initWithName:aname delay:d] autorelease]; +} + +-(id) init +{ + return [self initWithFrames:nil delay:0]; +} + +-(id) initWithFrames:(NSArray*)frames +{ + return [self initWithFrames:frames delay:0]; +} + +-(id) initWithFrames:(NSArray*)array delay:(float)delay +{ + if( (self=[super init]) ) { + + delay_ = delay; + self.frames = [NSMutableArray arrayWithArray:array]; + } + return self; +} + +-(id) initWithName:(NSString*)name +{ + return [self initWithName:name delay:0 frames:nil]; +} + +-(id) initWithName:(NSString*)name frames:(NSArray*)frames +{ + return [self initWithName:name delay:0 frames:frames]; +} + +-(id) initWithName:(NSString*)t delay:(float)d +{ + return [self initWithName:t delay:d frames:nil]; +} + +-(id) initWithName:(NSString*)name delay:(float)delay frames:(NSArray*)array +{ + if( (self=[super init]) ) { + + delay_ = delay; + self.name = name; + self.frames = [NSMutableArray arrayWithArray:array]; + } + return self; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | frames=%d, delay:%f>", [self class], self, + [frames_ count], + delay_ + ]; +} + +-(void) dealloc +{ + CCLOGINFO( @"cocos2d: deallocing %@",self); + [name_ release]; + [frames_ release]; + [super dealloc]; +} + +-(void) addFrame:(CCSpriteFrame*)frame +{ + [frames_ addObject:frame]; +} + +-(void) addFrameWithFilename:(NSString*)filename +{ + CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:filename]; + CGRect rect = CGRectZero; + rect.size = texture.contentSize; + CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:texture rect:rect]; + [frames_ addObject:frame]; +} + +-(void) addFrameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect +{ + CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:texture rect:rect]; + [frames_ addObject:frame]; +} + +@end diff --git a/libs/cocos2d/CCAnimationCache.h b/libs/cocos2d/CCAnimationCache.h new file mode 100644 index 0000000..3a9b8ae --- /dev/null +++ b/libs/cocos2d/CCAnimationCache.h @@ -0,0 +1,63 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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 + +@class CCAnimation; + +/** Singleton that manages the Animations. + It saves in a cache the animations. You should use this class if you want to save your animations in a cache. + + Before v0.99.5, the recommend way was to save them on the CCSprite. Since v0.99.5, you should use this class instead. + + @since v0.99.5 + */ +@interface CCAnimationCache : NSObject +{ + NSMutableDictionary *animations_; +} + +/** Retruns ths shared instance of the Animation cache */ ++ (CCAnimationCache *) sharedAnimationCache; + +/** Purges the cache. It releases all the CCAnimation objects and the shared instance. + */ ++(void)purgeSharedAnimationCache; + +/** Adds a CCAnimation with a name. + */ +-(void) addAnimation:(CCAnimation*)animation name:(NSString*)name; + +/** Deletes a CCAnimation from the cache. + */ +-(void) removeAnimationByName:(NSString*)name; + +/** Returns a CCAnimation that was previously added. + If the name is not found it will return nil. + You should retain the returned copy if you are going to use it. + */ +-(CCAnimation*) animationByName:(NSString*)name; + +@end diff --git a/libs/cocos2d/CCAnimationCache.m b/libs/cocos2d/CCAnimationCache.m new file mode 100644 index 0000000..003bc63 --- /dev/null +++ b/libs/cocos2d/CCAnimationCache.m @@ -0,0 +1,100 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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 "ccMacros.h" +#import "CCAnimationCache.h" +#import "CCAnimation.h" +#import "CCSprite.h" + + +@implementation CCAnimationCache + +#pragma mark CCAnimationCache - Alloc, Init & Dealloc + +static CCAnimationCache *sharedAnimationCache_=nil; + ++ (CCAnimationCache *)sharedAnimationCache +{ + if (!sharedAnimationCache_) + sharedAnimationCache_ = [[CCAnimationCache alloc] init]; + + return sharedAnimationCache_; +} + ++(id)alloc +{ + NSAssert(sharedAnimationCache_ == nil, @"Attempted to allocate a second instance of a singleton."); + return [super alloc]; +} + ++(void)purgeSharedAnimationCache +{ + [sharedAnimationCache_ release]; + sharedAnimationCache_ = nil; +} + +-(id) init +{ + if( (self=[super init]) ) { + animations_ = [[NSMutableDictionary alloc] initWithCapacity: 20]; + } + + return self; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | num of animations = %i>", [self class], self, [animations_ count]]; +} + +-(void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + + [animations_ release]; + [super dealloc]; +} + +#pragma mark CCAnimationCache - load/get/del + +-(void) addAnimation:(CCAnimation*)animation name:(NSString*)name +{ + [animations_ setObject:animation forKey:name]; +} + +-(void) removeAnimationByName:(NSString*)name +{ + if( ! name ) + return; + + [animations_ removeObjectForKey:name]; +} + +-(CCAnimation*) animationByName:(NSString*)name +{ + return [animations_ objectForKey:name]; +} + +@end diff --git a/libs/cocos2d/CCAtlasNode.h b/libs/cocos2d/CCAtlasNode.h new file mode 100644 index 0000000..e39e453 --- /dev/null +++ b/libs/cocos2d/CCAtlasNode.h @@ -0,0 +1,86 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CCTextureAtlas.h" +#import "CCNode.h" +#import "CCProtocols.h" + +/** CCAtlasNode is a subclass of CCNode that implements the CCRGBAProtocol and + CCTextureProtocol protocol + + It knows how to render a TextureAtlas object. + If you are going to render a TextureAtlas consider subclassing CCAtlasNode (or a subclass of CCAtlasNode) + + All features from CCNode are valid, plus the following features: + - opacity and RGB colors + */ +@interface CCAtlasNode : CCNode +{ + // texture atlas + CCTextureAtlas *textureAtlas_; + + // chars per row + NSUInteger itemsPerRow_; + // chars per column + NSUInteger itemsPerColumn_; + + // width of each char + NSUInteger itemWidth_; + // height of each char + NSUInteger itemHeight_; + + // blend function + ccBlendFunc blendFunc_; + + // texture RGBA. + GLubyte opacity_; + ccColor3B color_; + ccColor3B colorUnmodified_; + BOOL opacityModifyRGB_; +} + +/** conforms to CCTextureProtocol protocol */ +@property (nonatomic,readwrite,retain) CCTextureAtlas *textureAtlas; + +/** conforms to CCTextureProtocol protocol */ +@property (nonatomic,readwrite) ccBlendFunc blendFunc; + +/** conforms to CCRGBAProtocol protocol */ +@property (nonatomic,readwrite) GLubyte opacity; +/** conforms to CCRGBAProtocol protocol */ +@property (nonatomic,readwrite) ccColor3B color; + + +/** creates a CCAtlasNode with an Atlas file the width and height of each item measured in points and the quantity of items to render*/ ++(id) atlasWithTileFile:(NSString*)tile tileWidth:(NSUInteger)w tileHeight:(NSUInteger)h itemsToRender: (NSUInteger) c; + +/** initializes an CCAtlasNode with an Atlas file the width and height of each item measured in points and the quantity of items to render*/ +-(id) initWithTileFile:(NSString*)tile tileWidth:(NSUInteger)w tileHeight:(NSUInteger)h itemsToRender: (NSUInteger) c; + +/** updates the Atlas (indexed vertex array). + * Shall be overriden in subclasses + */ +-(void) updateAtlasValues; +@end diff --git a/libs/cocos2d/CCAtlasNode.m b/libs/cocos2d/CCAtlasNode.m new file mode 100644 index 0000000..184e1fc --- /dev/null +++ b/libs/cocos2d/CCAtlasNode.m @@ -0,0 +1,205 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CCAtlasNode.h" +#import "ccMacros.h" + + +@interface CCAtlasNode () +-(void) calculateMaxItems; +-(void) updateBlendFunc; +-(void) updateOpacityModifyRGB; +@end + +@implementation CCAtlasNode + +@synthesize textureAtlas = textureAtlas_; +@synthesize blendFunc = blendFunc_; + +#pragma mark CCAtlasNode - Creation & Init ++(id) atlasWithTileFile:(NSString*)tile tileWidth:(NSUInteger)w tileHeight:(NSUInteger)h itemsToRender: (NSUInteger) c +{ + return [[[self alloc] initWithTileFile:tile tileWidth:w tileHeight:h itemsToRender:c] autorelease]; +} + +-(id) initWithTileFile:(NSString*)tile tileWidth:(NSUInteger)w tileHeight:(NSUInteger)h itemsToRender: (NSUInteger) c +{ + if( (self=[super init]) ) { + + itemWidth_ = w * CC_CONTENT_SCALE_FACTOR(); + itemHeight_ = h * CC_CONTENT_SCALE_FACTOR(); + + opacity_ = 255; + color_ = colorUnmodified_ = ccWHITE; + opacityModifyRGB_ = YES; + + blendFunc_.src = CC_BLEND_SRC; + blendFunc_.dst = CC_BLEND_DST; + + // double retain to avoid the autorelease pool + // also, using: self.textureAtlas supports re-initialization without leaking + self.textureAtlas = [[CCTextureAtlas alloc] initWithFile:tile capacity:c]; + [textureAtlas_ release]; + + if( ! textureAtlas_ ) { + CCLOG(@"cocos2d: Could not initialize CCAtlasNode. Invalid Texture"); + [self release]; + return nil; + } + + [self updateBlendFunc]; + [self updateOpacityModifyRGB]; + + [self calculateMaxItems]; + + } + return self; +} + +-(void) dealloc +{ + [textureAtlas_ release]; + + [super dealloc]; +} + +#pragma mark CCAtlasNode - Atlas generation + +-(void) calculateMaxItems +{ + CGSize s = [[textureAtlas_ texture] contentSizeInPixels]; + itemsPerColumn_ = s.height / itemHeight_; + itemsPerRow_ = s.width / itemWidth_; +} + +-(void) updateAtlasValues +{ + [NSException raise:@"CCAtlasNode:Abstract" format:@"updateAtlasValue not overriden"]; +} + +#pragma mark CCAtlasNode - draw +- (void) draw +{ + // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY + // Unneeded states: GL_COLOR_ARRAY + glDisableClientState(GL_COLOR_ARRAY); + + glColor4ub( color_.r, color_.g, color_.b, opacity_); + + BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST; + if( newBlend ) + glBlendFunc( blendFunc_.src, blendFunc_.dst ); + + [textureAtlas_ drawQuads]; + + if( newBlend ) + glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + + // is this chepear than saving/restoring color state ? + // XXX: There is no need to restore the color to (255,255,255,255). Objects should use the color + // XXX: that they need +// glColor4ub( 255, 255, 255, 255); + + // restore default GL state + glEnableClientState(GL_COLOR_ARRAY); + +} + +#pragma mark CCAtlasNode - RGBA protocol + +- (ccColor3B) color +{ + if(opacityModifyRGB_) + return colorUnmodified_; + + return color_; +} + +-(void) setColor:(ccColor3B)color3 +{ + color_ = colorUnmodified_ = color3; + + if( opacityModifyRGB_ ){ + color_.r = color3.r * opacity_/255; + color_.g = color3.g * opacity_/255; + color_.b = color3.b * opacity_/255; + } +} + +-(GLubyte) opacity +{ + return opacity_; +} + +-(void) setOpacity:(GLubyte) anOpacity +{ + opacity_ = anOpacity; + + // special opacity for premultiplied textures + if( opacityModifyRGB_ ) + [self setColor: colorUnmodified_]; +} + +-(void) setOpacityModifyRGB:(BOOL)modify +{ + ccColor3B oldColor = self.color; + opacityModifyRGB_ = modify; + self.color = oldColor; +} + +-(BOOL) doesOpacityModifyRGB +{ + return opacityModifyRGB_; +} + +-(void) updateOpacityModifyRGB +{ + opacityModifyRGB_ = [textureAtlas_.texture hasPremultipliedAlpha]; +} + +#pragma mark CCAtlasNode - CocosNodeTexture protocol + +-(void) updateBlendFunc +{ + if( ! [textureAtlas_.texture hasPremultipliedAlpha] ) { + blendFunc_.src = GL_SRC_ALPHA; + blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; + } +} + +-(void) setTexture:(CCTexture2D*)texture +{ + textureAtlas_.texture = texture; + [self updateBlendFunc]; + [self updateOpacityModifyRGB]; +} + +-(CCTexture2D*) texture +{ + return textureAtlas_.texture; +} + +@end diff --git a/libs/cocos2d/CCBlockSupport.h b/libs/cocos2d/CCBlockSupport.h new file mode 100644 index 0000000..339d5aa --- /dev/null +++ b/libs/cocos2d/CCBlockSupport.h @@ -0,0 +1,51 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Stuart Carnie + * + * 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 + +/** @file + cocos2d blocks support + */ + +// To comply with Apple Objective C runtime (this is defined in NSObjCRuntime.h) +#if !defined(NS_BLOCKS_AVAILABLE) + #if __BLOCKS__ + #define NS_BLOCKS_AVAILABLE 1 + #else + #define NS_BLOCKS_AVAILABLE 0 + #endif +#endif + +#if NS_BLOCKS_AVAILABLE + +@interface NSObject(CCBlocksAdditions) + +- (void)ccCallbackBlock; +- (void)ccCallbackBlockWithSender:(id)sender; + +@end + +#endif // NS_BLOCKS_AVAILABLE diff --git a/libs/cocos2d/CCBlockSupport.m b/libs/cocos2d/CCBlockSupport.m new file mode 100644 index 0000000..9ac99b3 --- /dev/null +++ b/libs/cocos2d/CCBlockSupport.m @@ -0,0 +1,46 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Stuart Carnie + * + * 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 "CCBlockSupport.h" + +#if NS_BLOCKS_AVAILABLE + +@implementation NSObject(CCBlocksAdditions) + +- (void)ccCallbackBlock { + void (^block)(void) = (id)self; + block(); +} + +- (void)ccCallbackBlockWithSender:(id)sender { + void (^block)(id) = (id)self; + block(sender); +} + + +@end + +#endif diff --git a/libs/cocos2d/CCCamera.h b/libs/cocos2d/CCCamera.h new file mode 100644 index 0000000..387c854 --- /dev/null +++ b/libs/cocos2d/CCCamera.h @@ -0,0 +1,94 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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" + +/** + A CCCamera is used in every CCNode. + Useful to look at the object from different views. + The OpenGL gluLookAt() function is used to locate the + camera. + + If the object is transformed by any of the scale, rotation or + position attributes, then they will override the camera. + + IMPORTANT: Either your use the camera or the rotation/scale/position properties. You can't use both. + World coordinates won't work if you use the camera. + + Limitations: + + - Some nodes, like CCParallaxNode, CCParticle uses world node coordinates, and they won't work properly if you move them (or any of their ancestors) + using the camera. + + - It doesn't work on batched nodes like CCSprite objects when they are parented to a CCSpriteBatchNode object. + + - It is recommended to use it ONLY if you are going to create 3D effects. For 2D effecs, use the action CCFollow or position/scale/rotate. + +*/ + +@interface CCCamera : NSObject +{ + float eyeX_; + float eyeY_; + float eyeZ_; + + float centerX_; + float centerY_; + float centerZ_; + + float upX_; + float upY_; + float upZ_; + + BOOL dirty_; +} + +/** whether of not the camera is dirty */ +@property (nonatomic,readwrite) BOOL dirty; + +/** returns the Z eye */ ++(float) getZEye; + +/** sets the camera in the defaul position */ +-(void) restore; +/** Sets the camera using gluLookAt using its eye, center and up_vector */ +-(void) locate; +/** sets the eye values in points */ +-(void) setEyeX: (float)x eyeY:(float)y eyeZ:(float)z; +/** sets the center values in points */ +-(void) setCenterX: (float)x centerY:(float)y centerZ:(float)z; +/** sets the up values */ +-(void) setUpX: (float)x upY:(float)y upZ:(float)z; + +/** get the eye vector values in points */ +-(void) eyeX:(float*)x eyeY:(float*)y eyeZ:(float*)z; +/** get the center vector values in points */ +-(void) centerX:(float*)x centerY:(float*)y centerZ:(float*)z; +/** get the up vector values */ +-(void) upX:(float*)x upY:(float*)y upZ:(float*)z; + + +@end diff --git a/libs/cocos2d/CCCamera.m b/libs/cocos2d/CCCamera.m new file mode 100644 index 0000000..3841ab3 --- /dev/null +++ b/libs/cocos2d/CCCamera.m @@ -0,0 +1,130 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "Platforms/CCGL.h" +#import "CCCamera.h" +#import "ccMacros.h" +#import "CCDrawingPrimitives.h" + +@implementation CCCamera + +@synthesize dirty = dirty_; + +-(id) init +{ + if( (self=[super init]) ) + [self restore]; + + return self; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | center = (%.2f,%.2f,%.2f)>", [self class], self, centerX_, centerY_, centerZ_]; +} + + +- (void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + [super dealloc]; +} + +-(void) restore +{ + eyeX_ = eyeY_ = 0; + eyeZ_ = [CCCamera getZEye]; + + centerX_ = centerY_ = centerZ_ = 0; + + upX_ = 0.0f; + upY_ = 1.0f; + upZ_ = 0.0f; + + dirty_ = NO; +} + +-(void) locate +{ + if( dirty_ ) + gluLookAt( eyeX_, eyeY_, eyeZ_, + centerX_, centerY_, centerZ_, + upX_, upY_, upZ_ + ); +} + ++(float) getZEye +{ + return FLT_EPSILON; +// CGSize s = [[CCDirector sharedDirector] displaySize]; +// return ( s.height / 1.1566f ); +} + +-(void) setEyeX: (float)x eyeY:(float)y eyeZ:(float)z +{ + eyeX_ = x * CC_CONTENT_SCALE_FACTOR(); + eyeY_ = y * CC_CONTENT_SCALE_FACTOR(); + eyeZ_ = z * CC_CONTENT_SCALE_FACTOR(); + dirty_ = YES; +} + +-(void) setCenterX: (float)x centerY:(float)y centerZ:(float)z +{ + centerX_ = x * CC_CONTENT_SCALE_FACTOR(); + centerY_ = y * CC_CONTENT_SCALE_FACTOR(); + centerZ_ = z * CC_CONTENT_SCALE_FACTOR(); + dirty_ = YES; +} + +-(void) setUpX: (float)x upY:(float)y upZ:(float)z +{ + upX_ = x; + upY_ = y; + upZ_ = z; + dirty_ = YES; +} + +-(void) eyeX: (float*)x eyeY:(float*)y eyeZ:(float*)z +{ + *x = eyeX_ / CC_CONTENT_SCALE_FACTOR(); + *y = eyeY_ / CC_CONTENT_SCALE_FACTOR(); + *z = eyeZ_ / CC_CONTENT_SCALE_FACTOR(); +} + +-(void) centerX: (float*)x centerY:(float*)y centerZ:(float*)z +{ + *x = centerX_ / CC_CONTENT_SCALE_FACTOR(); + *y = centerY_ / CC_CONTENT_SCALE_FACTOR(); + *z = centerZ_ / CC_CONTENT_SCALE_FACTOR(); +} + +-(void) upX: (float*)x upY:(float*)y upZ:(float*)z +{ + *x = upX_; + *y = upY_; + *z = upZ_; +} + +@end diff --git a/libs/cocos2d/CCConfiguration.h b/libs/cocos2d/CCConfiguration.h new file mode 100644 index 0000000..11bd120 --- /dev/null +++ b/libs/cocos2d/CCConfiguration.h @@ -0,0 +1,110 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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 + +#import "Platforms/CCGL.h" + +/** OS version definitions. Includes both iOS and Mac OS versions + */ +enum { + kCCiOSVersion_3_0 = 0x03000000, + kCCiOSVersion_3_1 = 0x03010000, + kCCiOSVersion_3_1_1 = 0x03010100, + kCCiOSVersion_3_1_2 = 0x03010200, + kCCiOSVersion_3_1_3 = 0x03010300, + kCCiOSVersion_3_2 = 0x03020000, + kCCiOSVersion_3_2_1 = 0x03020100, + kCCiOSVersion_4_0 = 0x04000000, + kCCiOSVersion_4_0_1 = 0x04000100, + kCCiOSVersion_4_1 = 0x04010000, + + kCCMacVersion_10_5 = 0x0a050000, + kCCMacVersion_10_6 = 0x0a060000, + kCCMacVersion_10_7 = 0x0a070000, +}; + +/** + CCConfiguration contains some openGL variables + @since v0.99.0 + */ +@interface CCConfiguration : NSObject { + + GLint maxTextureSize_; + GLint maxModelviewStackDepth_; + BOOL supportsPVRTC_; + BOOL supportsNPOT_; + BOOL supportsBGRA8888_; + BOOL supportsDiscardFramebuffer_; + unsigned int OSVersion_; + GLint maxSamplesAllowed_; +} + +/** OpenGL Max texture size. */ +@property (nonatomic, readonly) GLint maxTextureSize; + +/** OpenGL Max Modelview Stack Depth. */ +@property (nonatomic, readonly) GLint maxModelviewStackDepth; + +/** Whether or not the GPU supports NPOT (Non Power Of Two) textures. + NPOT textures have the following limitations: + - They can't have mipmaps + - They only accept GL_CLAMP_TO_EDGE in GL_TEXTURE_WRAP_{S,T} + + @since v0.99.2 + */ +@property (nonatomic, readonly) BOOL supportsNPOT; + +/** Whether or not PVR Texture Compressed is supported */ +@property (nonatomic, readonly) BOOL supportsPVRTC; + +/** Whether or not BGRA8888 textures are supported. + + @since v0.99.2 + */ +@property (nonatomic, readonly) BOOL supportsBGRA8888; + +/** Whether or not glDiscardFramebufferEXT is supported + + @since v0.99.2 + */ +@property (nonatomic, readonly) BOOL supportsDiscardFramebuffer; + +/** returns the OS version. + - On iOS devices it returns the firmware version. + - On Mac returns the OS version + + @since v0.99.5 + */ +@property (nonatomic, readonly) unsigned int OSVersion; + +/** returns a shared instance of the CCConfiguration */ ++(CCConfiguration *) sharedConfiguration; + +/** returns whether or not an OpenGL is supported */ +- (BOOL) checkForGLExtension:(NSString *)searchName; + + + +@end diff --git a/libs/cocos2d/CCConfiguration.m b/libs/cocos2d/CCConfiguration.m new file mode 100644 index 0000000..e70bed1 --- /dev/null +++ b/libs/cocos2d/CCConfiguration.m @@ -0,0 +1,192 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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 + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import // Needed for UIDevice +#endif + +#import "Platforms/CCGL.h" +#import "CCBlockSupport.h" +#import "CCConfiguration.h" +#import "ccMacros.h" +#import "ccConfig.h" +#import "Support/OpenGL_Internal.h" + +@implementation CCConfiguration + +@synthesize maxTextureSize = maxTextureSize_; +@synthesize supportsPVRTC = supportsPVRTC_; +@synthesize maxModelviewStackDepth = maxModelviewStackDepth_; +@synthesize supportsNPOT = supportsNPOT_; +@synthesize supportsBGRA8888 = supportsBGRA8888_; +@synthesize supportsDiscardFramebuffer = supportsDiscardFramebuffer_; +@synthesize OSVersion = OSVersion_; + +// +// singleton stuff +// +static CCConfiguration *_sharedConfiguration = nil; + +static char * glExtensions; + ++ (CCConfiguration *)sharedConfiguration +{ + if (!_sharedConfiguration) + _sharedConfiguration = [[self alloc] init]; + + return _sharedConfiguration; +} + ++(id)alloc +{ + NSAssert(_sharedConfiguration == nil, @"Attempted to allocate a second instance of a singleton."); + return [super alloc]; +} + + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +- (NSString*)getMacVersion +{ + SInt32 versionMajor, versionMinor, versionBugFix; + Gestalt(gestaltSystemVersionMajor, &versionMajor); + Gestalt(gestaltSystemVersionMinor, &versionMinor); + Gestalt(gestaltSystemVersionBugFix, &versionBugFix); + + return [NSString stringWithFormat:@"%d.%d.%d", versionMajor, versionMinor, versionBugFix]; +} +#endif // __MAC_OS_X_VERSION_MAX_ALLOWED + +-(id) init +{ + if( (self=[super init])) { + + // Obtain iOS version + OSVersion_ = 0; +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + NSString *OSVer = [[UIDevice currentDevice] systemVersion]; +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + NSString *OSVer = [self getMacVersion]; +#endif + NSArray *arr = [OSVer componentsSeparatedByString:@"."]; + int idx=0x01000000; + for( NSString *str in arr ) { + int value = [str intValue]; + OSVersion_ += value * idx; + idx = idx >> 8; + } + CCLOG(@"cocos2d: OS version: %@ (0x%08x)", OSVer, OSVersion_); + + CCLOG(@"cocos2d: GL_VENDOR: %s", glGetString(GL_VENDOR) ); + CCLOG(@"cocos2d: GL_RENDERER: %s", glGetString ( GL_RENDERER ) ); + CCLOG(@"cocos2d: GL_VERSION: %s", glGetString ( GL_VERSION ) ); + + glExtensions = (char*) glGetString(GL_EXTENSIONS); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize_); + glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &maxModelviewStackDepth_); +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + if( OSVersion_ >= kCCiOSVersion_4_0 ) + glGetIntegerv(GL_MAX_SAMPLES_APPLE, &maxSamplesAllowed_); + else + maxSamplesAllowed_ = 0; +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + glGetIntegerv(GL_MAX_SAMPLES, &maxSamplesAllowed_); +#endif + + supportsPVRTC_ = [self checkForGLExtension:@"GL_IMG_texture_compression_pvrtc"]; +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + supportsNPOT_ = [self checkForGLExtension:@"GL_APPLE_texture_2D_limited_npot"]; +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + supportsNPOT_ = [self checkForGLExtension:@"GL_ARB_texture_non_power_of_two"]; +#endif + // It seems that somewhere between firmware iOS 3.0 and 4.2 Apple renamed + // GL_IMG_... to GL_APPLE.... So we should check both names + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + BOOL bgra8a = [self checkForGLExtension:@"GL_IMG_texture_format_BGRA8888"]; + BOOL bgra8b = [self checkForGLExtension:@"GL_APPLE_texture_format_BGRA8888"]; + supportsBGRA8888_ = bgra8a | bgra8b; +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + supportsBGRA8888_ = [self checkForGLExtension:@"GL_EXT_bgra"]; +#endif + + supportsDiscardFramebuffer_ = [self checkForGLExtension:@"GL_EXT_discard_framebuffer"]; + + CCLOG(@"cocos2d: GL_MAX_TEXTURE_SIZE: %d", maxTextureSize_); + CCLOG(@"cocos2d: GL_MAX_MODELVIEW_STACK_DEPTH: %d",maxModelviewStackDepth_); + CCLOG(@"cocos2d: GL_MAX_SAMPLES: %d", maxSamplesAllowed_); + CCLOG(@"cocos2d: GL supports PVRTC: %s", (supportsPVRTC_ ? "YES" : "NO") ); + CCLOG(@"cocos2d: GL supports BGRA8888 textures: %s", (supportsBGRA8888_ ? "YES" : "NO") ); + CCLOG(@"cocos2d: GL supports NPOT textures: %s", (supportsNPOT_ ? "YES" : "NO") ); + CCLOG(@"cocos2d: GL supports discard_framebuffer: %s", (supportsDiscardFramebuffer_ ? "YES" : "NO") ); + CCLOG(@"cocos2d: compiled with NPOT support: %s", +#if CC_TEXTURE_NPOT_SUPPORT + "YES" +#else + "NO" +#endif + ); + CCLOG(@"cocos2d: compiled with VBO support in TextureAtlas : %s", +#if CC_USES_VBO + "YES" +#else + "NO" +#endif + ); + + CCLOG(@"cocos2d: compiled with Affine Matrix transformation in CCNode : %s", +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + "YES" +#else + "NO" +#endif + ); + + CCLOG(@"cocos2d: compiled with Profiling Support: %s", +#if CC_ENABLE_PROFILERS + + "YES - *** Disable it when you finish profiling ***" +#else + "NO" +#endif + ); + + CHECK_GL_ERROR(); + } + + return self; +} + +- (BOOL) checkForGLExtension:(NSString *)searchName +{ + // For best results, extensionsNames should be stored in your renderer so that it does not + // need to be recreated on each invocation. + NSString *extensionsString = [NSString stringWithCString:glExtensions encoding: NSASCIIStringEncoding]; + NSArray *extensionsNames = [extensionsString componentsSeparatedByString:@" "]; + return [extensionsNames containsObject: searchName]; +} +@end diff --git a/libs/cocos2d/CCDirector.h b/libs/cocos2d/CCDirector.h new file mode 100644 index 0000000..fd36364 --- /dev/null +++ b/libs/cocos2d/CCDirector.h @@ -0,0 +1,304 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "ccConfig.h" +#import "ccTypes.h" + +// OpenGL related +#import "Platforms/CCGL.h" +#import "CCProtocols.h" + +/** @typedef ccDirectorProjection + Possible OpenGL projections used by director + */ +typedef enum { + /// sets a 2D projection (orthogonal projection). + kCCDirectorProjection2D, + + /// sets a 3D projection with a fovy=60, znear=0.5f and zfar=1500. + kCCDirectorProjection3D, + + /// it calls "updateProjection" on the projection delegate. + kCCDirectorProjectionCustom, + + /// Detault projection is 3D projection + kCCDirectorProjectionDefault = kCCDirectorProjection3D, + + // backward compatibility stuff + CCDirectorProjection2D = kCCDirectorProjection2D, + CCDirectorProjection3D = kCCDirectorProjection3D, + CCDirectorProjectionCustom = kCCDirectorProjectionCustom, + +} ccDirectorProjection; + + +@class CCLabelAtlas; +@class CCScene; + +/**Class that creates and handle the main Window and manages how +and when to execute the Scenes. + + The CCDirector is also resposible for: + - initializing the OpenGL ES context + - setting the OpenGL pixel format (default on is RGB565) + - setting the OpenGL buffer depth (default one is 0-bit) + - setting the projection (default one is 3D) + - setting the orientation (default one is Protrait) + + Since the CCDirector is a singleton, the standard way to use it is by calling: + - [[CCDirector sharedDirector] methodName]; + + The CCDirector also sets the default OpenGL context: + - GL_TEXTURE_2D is enabled + - GL_VERTEX_ARRAY is enabled + - GL_COLOR_ARRAY is enabled + - GL_TEXTURE_COORD_ARRAY is enabled +*/ +@interface CCDirector : NSObject +{ + CC_GLVIEW *openGLView_; + + // internal timer + NSTimeInterval animationInterval_; + NSTimeInterval oldAnimationInterval_; + + /* display FPS ? */ + BOOL displayFPS_; + + NSUInteger frames_; + ccTime accumDt_; + ccTime frameRate_; +#if CC_DIRECTOR_FAST_FPS + CCLabelAtlas *FPSLabel_; +#endif + + /* is the running scene paused */ + BOOL isPaused_; + + /* The running scene */ + CCScene *runningScene_; + + /* This object will be visited after the scene. Useful to hook a notification node */ + id notificationNode_; + + /* will be the next 'runningScene' in the next frame + nextScene is a weak reference. */ + CCScene *nextScene_; + + /* If YES, then "old" scene will receive the cleanup message */ + BOOL sendCleanupToScene_; + + /* scheduled scenes */ + NSMutableArray *scenesStack_; + + /* last time the main loop was updated */ + struct timeval lastUpdate_; + /* delta time since last tick to main loop */ + ccTime dt; + /* whether or not the next delta time will be zero */ + BOOL nextDeltaTimeZero_; + + /* projection used */ + ccDirectorProjection projection_; + + /* Projection protocol delegate */ + id projectionDelegate_; + + /* window size in points */ + CGSize winSizeInPoints_; + + /* window size in pixels */ + CGSize winSizeInPixels_; + + /* the cocos2d running thread */ + NSThread *runningThread_; + + // profiler +#if CC_ENABLE_PROFILERS + ccTime accumDtForProfiler_; +#endif +} + +/** returns the cocos2d thread. + If you want to run any cocos2d task, run it in this thread. + On iOS usually it is the main thread. + @since v0.99.5 + */ +@property (readonly, nonatomic ) NSThread *runningThread; +/** The current running Scene. Director can only run one Scene at the time */ +@property (nonatomic,readonly) CCScene* runningScene; +/** The FPS value */ +@property (nonatomic,readwrite, assign) NSTimeInterval animationInterval; +/** Whether or not to display the FPS on the bottom-left corner */ +@property (nonatomic,readwrite, assign) BOOL displayFPS; +/** The OpenGLView, where everything is rendered */ +@property (nonatomic,readwrite,retain) CC_GLVIEW *openGLView; +/** whether or not the next delta time will be zero */ +@property (nonatomic,readwrite,assign) BOOL nextDeltaTimeZero; +/** Whether or not the Director is paused */ +@property (nonatomic,readonly) BOOL isPaused; +/** Sets an OpenGL projection + @since v0.8.2 + */ +@property (nonatomic,readwrite) ccDirectorProjection projection; + +/** Whether or not the replaced scene will receive the cleanup message. + If the new scene is pushed, then the old scene won't receive the "cleanup" message. + If the new scene replaces the old one, the it will receive the "cleanup" message. + @since v0.99.0 + */ +@property (nonatomic, readonly) BOOL sendCleanupToScene; + +/** This object will be visited after the main scene is visited. + This object MUST implement the "visit" selector. + Useful to hook a notification object, like CCNotifications (http://github.com/manucorporat/CCNotifications) + @since v0.99.5 + */ +@property (nonatomic, readwrite, retain) id notificationNode; + +/** This object will be called when the OpenGL projection is udpated and only when the kCCDirectorProjectionCustom projection is used. + @since v0.99.5 + */ +@property (nonatomic, readwrite, retain) id projectionDelegate; + +/** returns a shared instance of the director */ ++(CCDirector *)sharedDirector; + + + +// Window size + +/** returns the size of the OpenGL view in points. + It takes into account any possible rotation (device orientation) of the window + */ +- (CGSize) winSize; + +/** returns the size of the OpenGL view in pixels. + It takes into account any possible rotation (device orientation) of the window. + On Mac winSize and winSizeInPixels return the same value. + */ +- (CGSize) winSizeInPixels; +/** returns the display size of the OpenGL view in pixels. + It doesn't take into account any possible rotation of the window. + */ +-(CGSize) displaySizeInPixels; +/** changes the projection size */ +-(void) reshapeProjection:(CGSize)newWindowSize; + +/** converts a UIKit coordinate to an OpenGL coordinate + Useful to convert (multi) touchs coordinates to the current layout (portrait or landscape) + */ +-(CGPoint) convertToGL: (CGPoint) p; +/** converts an OpenGL coordinate to a UIKit coordinate + Useful to convert node points to window points for calls such as glScissor + */ +-(CGPoint) convertToUI:(CGPoint)p; + +/// XXX: missing description +-(float) getZEye; + +// Scene Management + +/**Enters the Director's main loop with the given Scene. + * Call it to run only your FIRST scene. + * Don't call it if there is already a running scene. + */ +- (void) runWithScene:(CCScene*) scene; + +/**Suspends the execution of the running scene, pushing it on the stack of suspended scenes. + * The new scene will be executed. + * Try to avoid big stacks of pushed scenes to reduce memory allocation. + * ONLY call it if there is a running scene. + */ +- (void) pushScene:(CCScene*) scene; + +/**Pops out a scene from the queue. + * This scene will replace the running one. + * The running scene will be deleted. If there are no more scenes in the stack the execution is terminated. + * ONLY call it if there is a running scene. + */ +- (void) popScene; + +/** Replaces the running scene with a new one. The running scene is terminated. + * ONLY call it if there is a running scene. + */ +-(void) replaceScene: (CCScene*) scene; + +/** Ends the execution, releases the running scene. + It doesn't remove the OpenGL view from its parent. You have to do it manually. + */ +-(void) end; + +/** Pauses the running scene. + The running scene will be _drawed_ but all scheduled timers will be paused + While paused, the draw rate will be 4 FPS to reduce CPU consuption + */ +-(void) pause; + +/** Resumes the paused scene + The scheduled timers will be activated again. + The "delta time" will be 0 (as if the game wasn't paused) + */ +-(void) resume; + +/** Stops the animation. Nothing will be drawn. The main loop won't be triggered anymore. + If you wan't to pause your animation call [pause] instead. + */ +-(void) stopAnimation; + +/** The main loop is triggered again. + Call this function only if [stopAnimation] was called earlier + @warning Dont' call this function to start the main loop. To run the main loop call runWithScene + */ +-(void) startAnimation; + +/** Draw the scene. + This method is called every frame. Don't call it manually. + */ +-(void) drawScene; + +// Memory Helper + +/** Removes all the cocos2d data that was cached automatically. + It will purge the CCTextureCache, CCBitmapFont cache. + IMPORTANT: The CCSpriteFrameCache won't be purged. If you want to purge it, you have to purge it manually. + @since v0.99.3 + */ +-(void) purgeCachedData; + +// OpenGL Helper + +/** sets the OpenGL default values */ +-(void) setGLDefaultValues; + +/** enables/disables OpenGL alpha blending */ +- (void) setAlphaBlending: (BOOL) on; +/** enables/disables OpenGL depth test */ +- (void) setDepthTest: (BOOL) on; + +// Profiler +-(void) showProfilers; + +@end diff --git a/libs/cocos2d/CCDirector.m b/libs/cocos2d/CCDirector.m new file mode 100644 index 0000000..d71865b --- /dev/null +++ b/libs/cocos2d/CCDirector.m @@ -0,0 +1,557 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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. + */ + + +/* Idea of decoupling Window from Director taken from OC3D project: http://code.google.com/p/oc3d/ + */ + +#import +#import + +// cocos2d imports +#import "CCDirector.h" +#import "CCScheduler.h" +#import "CCActionManager.h" +#import "CCTextureCache.h" +#import "CCAnimationCache.h" +#import "CCLabelAtlas.h" +#import "ccMacros.h" +#import "CCTransition.h" +#import "CCScene.h" +#import "CCSpriteFrameCache.h" +#import "CCTexture2D.h" +#import "CCLabelBMFont.h" +#import "CCLayer.h" + +// support imports +#import "Platforms/CCGL.h" +#import "Platforms/CCNS.h" + +#import "Support/OpenGL_Internal.h" +#import "Support/CGPointExtension.h" + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "Platforms/iOS/CCDirectorIOS.h" +#define CC_DIRECTOR_DEFAULT CCDirectorTimer +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import "Platforms/Mac/CCDirectorMac.h" +#define CC_DIRECTOR_DEFAULT CCDirectorDisplayLink +#endif + +#import "Support/CCProfiling.h" + +#define kDefaultFPS 60.0 // 60 frames per second + +extern NSString * cocos2dVersion(void); + + +@interface CCDirector (Private) +-(void) setNextScene; +// shows the FPS in the screen +-(void) showFPS; +// calculates delta time since last time it was called +-(void) calculateDeltaTime; +@end + +@implementation CCDirector + +@synthesize animationInterval = animationInterval_; +@synthesize runningScene = runningScene_; +@synthesize displayFPS = displayFPS_; +@synthesize nextDeltaTimeZero = nextDeltaTimeZero_; +@synthesize isPaused = isPaused_; +@synthesize sendCleanupToScene = sendCleanupToScene_; +@synthesize runningThread = runningThread_; +@synthesize notificationNode = notificationNode_; +@synthesize projectionDelegate = projectionDelegate_; +// +// singleton stuff +// +static CCDirector *_sharedDirector = nil; + ++ (CCDirector *)sharedDirector +{ + if (!_sharedDirector) { + + // + // Default Director is TimerDirector + // + if( [ [CCDirector class] isEqual:[self class]] ) + _sharedDirector = [[CC_DIRECTOR_DEFAULT alloc] init]; + else + _sharedDirector = [[self alloc] init]; + } + + return _sharedDirector; +} + ++(id)alloc +{ + NSAssert(_sharedDirector == nil, @"Attempted to allocate a second instance of a singleton."); + return [super alloc]; +} + +- (id) init +{ + CCLOG(@"cocos2d: %@", cocos2dVersion() ); + + if( (self=[super init]) ) { + + CCLOG(@"cocos2d: Using Director Type:%@", [self class]); + + // scenes + runningScene_ = nil; + nextScene_ = nil; + + notificationNode_ = nil; + + oldAnimationInterval_ = animationInterval_ = 1.0 / kDefaultFPS; + scenesStack_ = [[NSMutableArray alloc] initWithCapacity:10]; + + // Set default projection (3D) + projection_ = kCCDirectorProjectionDefault; + + // projection delegate if "Custom" projection is used + projectionDelegate_ = nil; + + // FPS + displayFPS_ = NO; + frames_ = 0; + + // paused ? + isPaused_ = NO; + + // running thread + runningThread_ = nil; + + winSizeInPixels_ = winSizeInPoints_ = CGSizeZero; + } + + return self; +} + +- (void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + +#if CC_DIRECTOR_FAST_FPS + [FPSLabel_ release]; +#endif + [runningScene_ release]; + [notificationNode_ release]; + [scenesStack_ release]; + + [projectionDelegate_ release]; + + _sharedDirector = nil; + + [super dealloc]; +} + +-(void) setGLDefaultValues +{ + // This method SHOULD be called only after openGLView_ was initialized + NSAssert( openGLView_, @"openGLView_ must be initialized"); + + [self setAlphaBlending: YES]; + [self setDepthTest: YES]; + [self setProjection: projection_]; + + // set other opengl default values + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + +#if CC_DIRECTOR_FAST_FPS + if (!FPSLabel_) { + CCTexture2DPixelFormat currentFormat = [CCTexture2D defaultAlphaPixelFormat]; + [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA4444]; + FPSLabel_ = [[CCLabelAtlas labelWithString:@"00.0" charMapFile:@"fps_images.png" itemWidth:16 itemHeight:24 startCharMap:'.'] retain]; + [CCTexture2D setDefaultAlphaPixelFormat:currentFormat]; + } +#endif // CC_DIRECTOR_FAST_FPS +} + +// +// Draw the Scene +// +- (void) drawScene +{ + // Override me +} + +-(void) calculateDeltaTime +{ + struct timeval now; + + if( gettimeofday( &now, NULL) != 0 ) { + CCLOG(@"cocos2d: error in gettimeofday"); + dt = 0; + return; + } + + // new delta time + if( nextDeltaTimeZero_ ) { + dt = 0; + nextDeltaTimeZero_ = NO; + } else { + dt = (now.tv_sec - lastUpdate_.tv_sec) + (now.tv_usec - lastUpdate_.tv_usec) / 1000000.0f; + dt = MAX(0,dt); + } + + lastUpdate_ = now; +} + +#pragma mark Director - Memory Helper + +-(void) purgeCachedData +{ + [CCLabelBMFont purgeCachedData]; + [[CCTextureCache sharedTextureCache] removeUnusedTextures]; +} + +#pragma mark Director - Scene OpenGL Helper + +-(ccDirectorProjection) projection +{ + return projection_; +} + +-(float) getZEye +{ + return ( winSizeInPixels_.height / 1.1566f ); +} + +-(void) setProjection:(ccDirectorProjection)projection +{ + CCLOG(@"cocos2d: override me"); +} + +- (void) setAlphaBlending: (BOOL) on +{ + if (on) { + glEnable(GL_BLEND); + glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + + } else + glDisable(GL_BLEND); +} + +- (void) setDepthTest: (BOOL) on +{ + if (on) { + ccglClearDepth(1.0f); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); +// glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + } else + glDisable( GL_DEPTH_TEST ); +} + +#pragma mark Director Integration with a UIKit view + +-(CC_GLVIEW*) openGLView +{ + return openGLView_; +} + +-(void) setOpenGLView:(CC_GLVIEW *)view +{ + NSAssert( view, @"OpenGLView must be non-nil"); + + if( view != openGLView_ ) { + [openGLView_ release]; + openGLView_ = [view retain]; + + // set size + winSizeInPixels_ = winSizeInPoints_ = CCNSSizeToCGSize( [view bounds].size ); + + [self setGLDefaultValues]; + } +} + +#pragma mark Director Scene Landscape + +-(CGPoint)convertToGL:(CGPoint)uiPoint +{ + CCLOG(@"CCDirector#convertToGL: OVERRIDE ME."); + return CGPointZero; +} + +-(CGPoint)convertToUI:(CGPoint)glPoint +{ + CCLOG(@"CCDirector#convertToUI: OVERRIDE ME."); + return CGPointZero; +} + +-(CGSize)winSize +{ + return winSizeInPoints_; +} + +-(CGSize)winSizeInPixels +{ + return winSizeInPixels_; +} + +-(CGSize)displaySizeInPixels +{ + return winSizeInPixels_; +} + +-(void) reshapeProjection:(CGSize)newWindowSize +{ + winSizeInPixels_ = winSizeInPoints_ = newWindowSize; + [self setProjection:projection_]; +} + +#pragma mark Director Scene Management + +- (void)runWithScene:(CCScene*) scene +{ + NSAssert( scene != nil, @"Argument must be non-nil"); + NSAssert( runningScene_ == nil, @"You can't run an scene if another Scene is running. Use replaceScene or pushScene instead"); + + [self pushScene:scene]; + [self startAnimation]; +} + +-(void) replaceScene: (CCScene*) scene +{ + NSAssert( scene != nil, @"Argument must be non-nil"); + + NSUInteger index = [scenesStack_ count]; + + sendCleanupToScene_ = YES; + [scenesStack_ replaceObjectAtIndex:index-1 withObject:scene]; + nextScene_ = scene; // nextScene_ is a weak ref +} + +- (void) pushScene: (CCScene*) scene +{ + NSAssert( scene != nil, @"Argument must be non-nil"); + + sendCleanupToScene_ = NO; + + [scenesStack_ addObject: scene]; + nextScene_ = scene; // nextScene_ is a weak ref +} + +-(void) popScene +{ + NSAssert( runningScene_ != nil, @"A running Scene is needed"); + + [scenesStack_ removeLastObject]; + NSUInteger c = [scenesStack_ count]; + + if( c == 0 ) + [self end]; + else { + sendCleanupToScene_ = YES; + nextScene_ = [scenesStack_ objectAtIndex:c-1]; + } +} + +-(void) end +{ + [runningScene_ onExit]; + [runningScene_ cleanup]; + [runningScene_ release]; + + runningScene_ = nil; + nextScene_ = nil; + + // remove all objects, but don't release it. + // runWithScene might be executed after 'end'. + [scenesStack_ removeAllObjects]; + + [self stopAnimation]; + +#if CC_DIRECTOR_FAST_FPS + [FPSLabel_ release]; + FPSLabel_ = nil; +#endif + + [projectionDelegate_ release]; + projectionDelegate_ = nil; + + // Purge bitmap cache + [CCLabelBMFont purgeCachedData]; + + // Purge all managers + [CCAnimationCache purgeSharedAnimationCache]; + [CCSpriteFrameCache purgeSharedSpriteFrameCache]; + [CCScheduler purgeSharedScheduler]; + [CCActionManager purgeSharedManager]; + [CCTextureCache purgeSharedTextureCache]; + + + // OpenGL view + + // Since the director doesn't attach the openglview to the window + // it shouldn't remove it from the window too. +// [openGLView_ removeFromSuperview]; + + [openGLView_ release]; + openGLView_ = nil; +} + +-(void) setNextScene +{ + Class transClass = [CCTransitionScene class]; + BOOL runningIsTransition = [runningScene_ isKindOfClass:transClass]; + BOOL newIsTransition = [nextScene_ isKindOfClass:transClass]; + + // If it is not a transition, call onExit/cleanup + if( ! newIsTransition ) { + [runningScene_ onExit]; + + // issue #709. the root node (scene) should receive the cleanup message too + // otherwise it might be leaked. + if( sendCleanupToScene_) + [runningScene_ cleanup]; + } + + [runningScene_ release]; + + runningScene_ = [nextScene_ retain]; + nextScene_ = nil; + + if( ! runningIsTransition ) { + [runningScene_ onEnter]; + [runningScene_ onEnterTransitionDidFinish]; + } +} + +-(void) pause +{ + if( isPaused_ ) + return; + + oldAnimationInterval_ = animationInterval_; + + // when paused, don't consume CPU + [self setAnimationInterval:1/4.0]; + isPaused_ = YES; +} + +-(void) resume +{ + if( ! isPaused_ ) + return; + + [self setAnimationInterval: oldAnimationInterval_]; + + if( gettimeofday( &lastUpdate_, NULL) != 0 ) { + CCLOG(@"cocos2d: Director: Error in gettimeofday"); + } + + isPaused_ = NO; + dt = 0; +} + +- (void)startAnimation +{ + CCLOG(@"cocos2d: Director#startAnimation. Override me"); +} + +- (void)stopAnimation +{ + CCLOG(@"cocos2d: Director#stopAnimation. Override me"); +} + +- (void)setAnimationInterval:(NSTimeInterval)interval +{ + CCLOG(@"cocos2d: Director#setAnimationInterval. Override me"); +} + +#if CC_DIRECTOR_FAST_FPS + +// display the FPS using a LabelAtlas +// updates the FPS every frame +-(void) showFPS +{ + frames_++; + accumDt_ += dt; + + if ( accumDt_ > CC_DIRECTOR_FPS_INTERVAL) { + frameRate_ = frames_/accumDt_; + frames_ = 0; + accumDt_ = 0; + +// sprintf(format,"%.1f",frameRate); +// [FPSLabel setCString:format]; + + NSString *str = [[NSString alloc] initWithFormat:@"%.1f", frameRate_]; + [FPSLabel_ setString:str]; + [str release]; + } + + [FPSLabel_ draw]; +} +#else +// display the FPS using a manually generated Texture (very slow) +// updates the FPS 3 times per second aprox. +-(void) showFPS +{ + frames_++; + accumDt_ += dt; + + if ( accumDt_ > CC_DIRECTOR_FPS_INTERVAL) { + frameRate_ = frames_/accumDt_; + frames_ = 0; + accumDt_ = 0; + } + + NSString *str = [NSString stringWithFormat:@"%.2f",frameRate_]; + CCTexture2D *texture = [[CCTexture2D alloc] initWithString:str dimensions:CGSizeMake(100,30) alignment:CCTextAlignmentLeft fontName:@"Arial" fontSize:24]; + + // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY + // Unneeded states: GL_COLOR_ARRAY + glDisableClientState(GL_COLOR_ARRAY); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glColor4ub(224,224,244,200); + [texture drawAtPoint: ccp(5,2)]; + [texture release]; + + glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + + // restore default GL state + glEnableClientState(GL_COLOR_ARRAY); +} +#endif + +- (void) showProfilers { +#if CC_ENABLE_PROFILERS + accumDtForProfiler_ += dt; + if (accumDtForProfiler_ > 1.0f) { + accumDtForProfiler_ = 0; + [[CCProfiler sharedProfiler] displayTimers]; + } +#endif // CC_ENABLE_PROFILERS +} + +@end + diff --git a/libs/cocos2d/CCDrawingPrimitives.h b/libs/cocos2d/CCDrawingPrimitives.h new file mode 100644 index 0000000..932547b --- /dev/null +++ b/libs/cocos2d/CCDrawingPrimitives.h @@ -0,0 +1,91 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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. + */ + + +#ifndef __CC_DRAWING_PRIMITIVES_H +#define __CC_DRAWING_PRIMITIVES_H + +#import +#import + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import // for CGPoint +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + @file + Drawing OpenGL ES primitives. + - ccDrawPoint + - ccDrawLine + - ccDrawPoly + - ccDrawCircle + - ccDrawQuadBezier + - ccDrawCubicBezier + + You can change the color, width and other property by calling the + glColor4ub(), glLineWidth(), glPointSize(). + + @warning These functions draws the Line, Point, Polygon, immediately. They aren't batched. If you are going to make a game that depends on these primitives, I suggest creating a batch. + */ + + +/** draws a point given x and y coordinate measured in points. */ +void ccDrawPoint( CGPoint point ); + +/** draws an array of points. + @since v0.7.2 + */ +void ccDrawPoints( const CGPoint *points, NSUInteger numberOfPoints ); + +/** draws a line given the origin and destination point measured in points. */ +void ccDrawLine( CGPoint origin, CGPoint destination ); + +/** draws a poligon given a pointer to CGPoint coordiantes and the number of vertices measured in points. + The polygon can be closed or open + */ +void ccDrawPoly( const CGPoint *vertices, NSUInteger numOfVertices, BOOL closePolygon ); + +/** draws a circle given the center, radius and number of segments measured in points */ +void ccDrawCircle( CGPoint center, float radius, float angle, NSUInteger segments, BOOL drawLineToCenter); + +/** draws a quad bezier path measured in points. + @since v0.8 + */ +void ccDrawQuadBezier(CGPoint origin, CGPoint control, CGPoint destination, NSUInteger segments); + +/** draws a cubic bezier path measured in points. + @since v0.8 + */ +void ccDrawCubicBezier(CGPoint origin, CGPoint control1, CGPoint control2, CGPoint destination, NSUInteger segments); + +#ifdef __cplusplus +} +#endif + +#endif // __CC_DRAWING_PRIMITIVES_H diff --git a/libs/cocos2d/CCDrawingPrimitives.m b/libs/cocos2d/CCDrawingPrimitives.m new file mode 100644 index 0000000..219df9d --- /dev/null +++ b/libs/cocos2d/CCDrawingPrimitives.m @@ -0,0 +1,271 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 +#import +#import + +#import "CCDrawingPrimitives.h" +#import "ccTypes.h" +#import "ccMacros.h" +#import "Platforms/CCGL.h" + +void ccDrawPoint( CGPoint point ) +{ + ccVertex2F p = (ccVertex2F) {point.x * CC_CONTENT_SCALE_FACTOR(), point.y * CC_CONTENT_SCALE_FACTOR() }; + + // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Needed states: GL_VERTEX_ARRAY, + // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY + glDisable(GL_TEXTURE_2D); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + glVertexPointer(2, GL_FLOAT, 0, &p); + glDrawArrays(GL_POINTS, 0, 1); + + // restore default state + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnable(GL_TEXTURE_2D); +} + +void ccDrawPoints( const CGPoint *points, NSUInteger numberOfPoints ) +{ + // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Needed states: GL_VERTEX_ARRAY, + // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY + glDisable(GL_TEXTURE_2D); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + ccVertex2F newPoints[numberOfPoints]; + + // iPhone and 32-bit machines optimization + if( sizeof(CGPoint) == sizeof(ccVertex2F) ) { + + // points ? + if( CC_CONTENT_SCALE_FACTOR() != 1 ) { + for( NSUInteger i=0; i + +@class CCTexture2D; + +/** FBO class that grabs the the contents of the screen */ +@interface CCGrabber : NSObject +{ + GLuint fbo; + GLint oldFBO; +} + +-(void)grab:(CCTexture2D*)texture; +-(void)beforeRender:(CCTexture2D*)texture; +-(void)afterRender:(CCTexture2D*)texture; + +@end diff --git a/libs/cocos2d/CCGrabber.m b/libs/cocos2d/CCGrabber.m new file mode 100644 index 0000000..a259091 --- /dev/null +++ b/libs/cocos2d/CCGrabber.m @@ -0,0 +1,95 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 On-Core + * + * 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 "Platforms/CCGL.h" +#import "CCGrabber.h" +#import "ccMacros.h" +#import "CCTexture2D.h" +#import "Support/OpenGL_Internal.h" + +@implementation CCGrabber + +-(id) init +{ + if(( self = [super init] )) { + // generate FBO + ccglGenFramebuffers(1, &fbo); + } + return self; +} + +-(void)grab:(CCTexture2D*)texture +{ + glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO); + + // bind + ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo); + + // associate texture with FBO + ccglFramebufferTexture2D(CC_GL_FRAMEBUFFER, CC_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.name, 0); + + // check if it worked (probably worth doing :) ) + GLuint status = ccglCheckFramebufferStatus(CC_GL_FRAMEBUFFER); + if (status != CC_GL_FRAMEBUFFER_COMPLETE) + [NSException raise:@"Frame Grabber" format:@"Could not attach texture to framebuffer"]; + + ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO); +} + +-(void)beforeRender:(CCTexture2D*)texture +{ + glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO); + ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo); + + // BUG XXX: doesn't work with RGB565. + + + glClearColor(0,0,0,0); + + // BUG #631: To fix #631, uncomment the lines with #631 + // Warning: But it CCGrabber won't work with 2 effects at the same time +// glClearColor(0.0f,0.0f,0.0f,1.0f); // #631 + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + +// glColorMask(TRUE, TRUE, TRUE, FALSE); // #631 + +} + +-(void)afterRender:(CCTexture2D*)texture +{ + ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO); +// glColorMask(TRUE, TRUE, TRUE, TRUE); // #631 +} + +- (void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + ccglDeleteFramebuffers(1, &fbo); + [super dealloc]; +} + +@end diff --git a/libs/cocos2d/CCGrid.h b/libs/cocos2d/CCGrid.h new file mode 100644 index 0000000..e5e77e8 --- /dev/null +++ b/libs/cocos2d/CCGrid.h @@ -0,0 +1,121 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 On-Core + * + * 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 + +#import "CCNode.h" +#import "CCCamera.h" +#import "ccTypes.h" + +@class CCTexture2D; +@class CCGrabber; + +/** Base class for other + */ +@interface CCGridBase : NSObject +{ + BOOL active_; + int reuseGrid_; + ccGridSize gridSize_; + CCTexture2D *texture_; + CGPoint step_; + CCGrabber *grabber_; + BOOL isTextureFlipped_; +} + +/** wheter or not the grid is active */ +@property (nonatomic,readwrite) BOOL active; +/** number of times that the grid will be reused */ +@property (nonatomic,readwrite) int reuseGrid; +/** size of the grid */ +@property (nonatomic,readonly) ccGridSize gridSize; +/** pixels between the grids */ +@property (nonatomic,readwrite) CGPoint step; +/** texture used */ +@property (nonatomic, retain) CCTexture2D *texture; +/** grabber used */ +@property (nonatomic, retain) CCGrabber *grabber; +/** is texture flipped */ +@property (nonatomic, readwrite) BOOL isTextureFlipped; + ++(id) gridWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped; ++(id) gridWithSize:(ccGridSize)gridSize; + +-(id) initWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped; +-(id)initWithSize:(ccGridSize)gridSize; +-(void)beforeDraw; +-(void)afterDraw:(CCNode*)target; +-(void)blit; +-(void)reuse; + +-(void)calculateVertexPoints; + +@end + +//////////////////////////////////////////////////////////// + +/** + CCGrid3D is a 3D grid implementation. Each vertex has 3 dimensions: x,y,z + */ +@interface CCGrid3D : CCGridBase +{ + GLvoid *texCoordinates; + GLvoid *vertices; + GLvoid *originalVertices; + GLushort *indices; +} + +/** returns the vertex at a given position */ +-(ccVertex3F)vertex:(ccGridSize)pos; +/** returns the original (non-transformed) vertex at a given position */ +-(ccVertex3F)originalVertex:(ccGridSize)pos; +/** sets a new vertex at a given position */ +-(void)setVertex:(ccGridSize)pos vertex:(ccVertex3F)vertex; + +@end + +//////////////////////////////////////////////////////////// + +/** + CCTiledGrid3D is a 3D grid implementation. It differs from Grid3D in that + the tiles can be separated from the grid. +*/ +@interface CCTiledGrid3D : CCGridBase +{ + GLvoid *texCoordinates; + GLvoid *vertices; + GLvoid *originalVertices; + GLushort *indices; +} + +/** returns the tile at the given position */ +-(ccQuad3)tile:(ccGridSize)pos; +/** returns the original tile (untransformed) at the given position */ +-(ccQuad3)originalTile:(ccGridSize)pos; +/** sets a new tile */ +-(void)setTile:(ccGridSize)pos coords:(ccQuad3)coords; + +@end diff --git a/libs/cocos2d/CCGrid.m b/libs/cocos2d/CCGrid.m new file mode 100644 index 0000000..c2ed19d --- /dev/null +++ b/libs/cocos2d/CCGrid.m @@ -0,0 +1,571 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 On-Core + * + * 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 + +#import "ccMacros.h" +#import "CCGrid.h" +#import "CCTexture2D.h" +#import "CCDirector.h" +#import "CCGrabber.h" + +#import "Platforms/CCGL.h" +#import "Support/CGPointExtension.h" +#import "Support/ccUtils.h" + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "Platforms/iOS/CCDirectorIOS.h" +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED + +#pragma mark - +#pragma mark CCGridBase + +@implementation CCGridBase + +@synthesize reuseGrid = reuseGrid_; +@synthesize texture = texture_; +@synthesize grabber = grabber_; +@synthesize gridSize = gridSize_; +@synthesize step = step_; + ++(id) gridWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped +{ + return [[[self alloc] initWithSize:gridSize texture:texture flippedTexture:flipped] autorelease]; +} + ++(id) gridWithSize:(ccGridSize)gridSize +{ + return [[(CCGridBase*)[self alloc] initWithSize:gridSize] autorelease]; +} + +-(id) initWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped +{ + if( (self=[super init]) ) { + + active_ = NO; + reuseGrid_ = 0; + gridSize_ = gridSize; + + self.texture = texture; + isTextureFlipped_ = flipped; + + CGSize texSize = [texture_ contentSizeInPixels]; + step_.x = texSize.width / gridSize_.x; + step_.y = texSize.height / gridSize_.y; + + grabber_ = [[CCGrabber alloc] init]; + [grabber_ grab:texture_]; + + [self calculateVertexPoints]; + } + return self; +} + +-(id)initWithSize:(ccGridSize)gSize +{ + CCDirector *director = [CCDirector sharedDirector]; + CGSize s = [director winSizeInPixels]; + + unsigned long POTWide = ccNextPOT(s.width); + unsigned long POTHigh = ccNextPOT(s.height); + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + EAGLView *glview = [[CCDirector sharedDirector] openGLView]; + NSString *pixelFormat = [glview pixelFormat]; + + CCTexture2DPixelFormat format = [pixelFormat isEqualToString: kEAGLColorFormatRGB565] ? kCCTexture2DPixelFormat_RGB565 : kCCTexture2DPixelFormat_RGBA8888; +#else + CCTexture2DPixelFormat format = kCCTexture2DPixelFormat_RGBA8888; +#endif + + void *data = calloc((int)(POTWide * POTHigh * 4), 1); + if( ! data ) { + CCLOG(@"cocos2d: CCGrid: not enough memory"); + [self release]; + return nil; + } + + CCTexture2D *texture = [[CCTexture2D alloc] initWithData:data pixelFormat:format pixelsWide:POTWide pixelsHigh:POTHigh contentSize:s]; + free( data ); + + if( ! texture ) { + CCLOG(@"cocos2d: CCGrid: error creating texture"); + [self release]; + return nil; + } + + self = [self initWithSize:gSize texture:texture flippedTexture:NO]; + + [texture release]; + + return self; +} +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | Dimensions = %ix%i>", [self class], self, gridSize_.x, gridSize_.y]; +} + +- (void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + + [self setActive: NO]; + + [texture_ release]; + [grabber_ release]; + [super dealloc]; +} + +// properties +-(BOOL) active +{ + return active_; +} + +-(void) setActive:(BOOL)active +{ + active_ = active; + if( ! active ) { + CCDirector *director = [CCDirector sharedDirector]; + ccDirectorProjection proj = [director projection]; + [director setProjection:proj]; + } +} + +-(BOOL) isTextureFlipped +{ + return isTextureFlipped_; +} + +-(void) setIsTextureFlipped:(BOOL)flipped +{ + if( isTextureFlipped_ != flipped ) { + isTextureFlipped_ = flipped; + [self calculateVertexPoints]; + } +} + +// This routine can be merged with Director +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +-(void)applyLandscape +{ + CCDirector *director = [CCDirector sharedDirector]; + + CGSize winSize = [director displaySizeInPixels]; + float w = winSize.width / 2; + float h = winSize.height / 2; + + ccDeviceOrientation orientation = [director deviceOrientation]; + + switch (orientation) { + case CCDeviceOrientationLandscapeLeft: + glTranslatef(w,h,0); + glRotatef(-90,0,0,1); + glTranslatef(-h,-w,0); + break; + case CCDeviceOrientationLandscapeRight: + glTranslatef(w,h,0); + glRotatef(90,0,0,1); + glTranslatef(-h,-w,0); + break; + case CCDeviceOrientationPortraitUpsideDown: + glTranslatef(w,h,0); + glRotatef(180,0,0,1); + glTranslatef(-w,-h,0); + break; + default: + break; + } +} +#endif + +-(void)set2DProjection +{ + CGSize winSize = [[CCDirector sharedDirector] winSizeInPixels]; + + glLoadIdentity(); + glViewport(0, 0, winSize.width, winSize.height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + ccglOrtho(0, winSize.width, 0, winSize.height, -1024, 1024); + glMatrixMode(GL_MODELVIEW); +} + +// This routine can be merged with Director +-(void)set3DProjection +{ + CCDirector *director = [CCDirector sharedDirector]; + + CGSize winSize = [director displaySizeInPixels]; + + glViewport(0, 0, winSize.width, winSize.height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(60, (GLfloat)winSize.width/winSize.height, 0.5f, 1500.0f); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt( winSize.width/2, winSize.height/2, [director getZEye], + winSize.width/2, winSize.height/2, 0, + 0.0f, 1.0f, 0.0f + ); +} + +-(void)beforeDraw +{ + [self set2DProjection]; + [grabber_ beforeRender:texture_]; +} + +-(void)afterDraw:(CCNode *)target +{ + [grabber_ afterRender:texture_]; + + [self set3DProjection]; +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + [self applyLandscape]; +#endif + + if( target.camera.dirty ) { + + CGPoint offset = [target anchorPointInPixels]; + + // + // XXX: Camera should be applied in the AnchorPoint + // + ccglTranslate(offset.x, offset.y, 0); + [target.camera locate]; + ccglTranslate(-offset.x, -offset.y, 0); + } + + glBindTexture(GL_TEXTURE_2D, texture_.name); + + [self blit]; +} + +-(void)blit +{ + [NSException raise:@"GridBase" format:@"Abstract class needs implementation"]; +} + +-(void)reuse +{ + [NSException raise:@"GridBase" format:@"Abstract class needs implementation"]; +} + +-(void)calculateVertexPoints +{ + [NSException raise:@"GridBase" format:@"Abstract class needs implementation"]; +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark CCGrid3D +@implementation CCGrid3D + +-(void)dealloc +{ + free(texCoordinates); + free(vertices); + free(indices); + free(originalVertices); + [super dealloc]; +} + +-(void)blit +{ + NSInteger n = gridSize_.x * gridSize_.y; + + // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY + // Unneeded states: GL_COLOR_ARRAY + glDisableClientState(GL_COLOR_ARRAY); + + glVertexPointer(3, GL_FLOAT, 0, vertices); + glTexCoordPointer(2, GL_FLOAT, 0, texCoordinates); + glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, indices); + + // restore GL default state + glEnableClientState(GL_COLOR_ARRAY); +} + +-(void)calculateVertexPoints +{ + float width = (float)texture_.pixelsWide; + float height = (float)texture_.pixelsHigh; + float imageH = texture_.contentSizeInPixels.height; + + int x, y, i; + + vertices = malloc((gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F)); + originalVertices = malloc((gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F)); + texCoordinates = malloc((gridSize_.x+1)*(gridSize_.y+1)*sizeof(CGPoint)); + indices = malloc(gridSize_.x*gridSize_.y*sizeof(GLushort)*6); + + float *vertArray = (float*)vertices; + float *texArray = (float*)texCoordinates; + GLushort *idxArray = (GLushort *)indices; + + for( x = 0; x < gridSize_.x; x++ ) + { + for( y = 0; y < gridSize_.y; y++ ) + { + NSInteger idx = (y * gridSize_.x) + x; + + float x1 = x * step_.x; + float x2 = x1 + step_.x; + float y1 = y * step_.y; + float y2 = y1 + step_.y; + + GLushort a = x * (gridSize_.y+1) + y; + GLushort b = (x+1) * (gridSize_.y+1) + y; + GLushort c = (x+1) * (gridSize_.y+1) + (y+1); + GLushort d = x * (gridSize_.y+1) + (y+1); + + GLushort tempidx[6] = { a, b, d, b, c, d }; + + memcpy(&idxArray[6*idx], tempidx, 6*sizeof(GLushort)); + + int l1[4] = { a*3, b*3, c*3, d*3 }; + ccVertex3F e = {x1,y1,0}; + ccVertex3F f = {x2,y1,0}; + ccVertex3F g = {x2,y2,0}; + ccVertex3F h = {x1,y2,0}; + + ccVertex3F l2[4] = { e, f, g, h }; + + int tex1[4] = { a*2, b*2, c*2, d*2 }; + CGPoint tex2[4] = { ccp(x1, y1), ccp(x2, y1), ccp(x2, y2), ccp(x1, y2) }; + + for( i = 0; i < 4; i++ ) + { + vertArray[ l1[i] ] = l2[i].x; + vertArray[ l1[i] + 1 ] = l2[i].y; + vertArray[ l1[i] + 2 ] = l2[i].z; + + texArray[ tex1[i] ] = tex2[i].x / width; + if( isTextureFlipped_ ) + texArray[ tex1[i] + 1 ] = (imageH - tex2[i].y) / height; + else + texArray[ tex1[i] + 1 ] = tex2[i].y / height; + } + } + } + + memcpy(originalVertices, vertices, (gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F)); +} + +-(ccVertex3F)vertex:(ccGridSize)pos +{ + NSInteger index = (pos.x * (gridSize_.y+1) + pos.y) * 3; + float *vertArray = (float *)vertices; + + ccVertex3F vert = { vertArray[index], vertArray[index+1], vertArray[index+2] }; + + return vert; +} + +-(ccVertex3F)originalVertex:(ccGridSize)pos +{ + NSInteger index = (pos.x * (gridSize_.y+1) + pos.y) * 3; + float *vertArray = (float *)originalVertices; + + ccVertex3F vert = { vertArray[index], vertArray[index+1], vertArray[index+2] }; + + return vert; +} + +-(void)setVertex:(ccGridSize)pos vertex:(ccVertex3F)vertex +{ + NSInteger index = (pos.x * (gridSize_.y+1) + pos.y) * 3; + float *vertArray = (float *)vertices; + vertArray[index] = vertex.x; + vertArray[index+1] = vertex.y; + vertArray[index+2] = vertex.z; +} + +-(void)reuse +{ + if ( reuseGrid_ > 0 ) + { + memcpy(originalVertices, vertices, (gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F)); + reuseGrid_--; + } +} + +@end + +//////////////////////////////////////////////////////////// + +#pragma mark - +#pragma mark CCTiledGrid3D + +@implementation CCTiledGrid3D + +-(void)dealloc +{ + free(texCoordinates); + free(vertices); + free(indices); + free(originalVertices); + [super dealloc]; +} + +-(void)blit +{ + NSInteger n = gridSize_.x * gridSize_.y; + + // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY + // Unneeded states: GL_COLOR_ARRAY + glDisableClientState(GL_COLOR_ARRAY); + + glVertexPointer(3, GL_FLOAT, 0, vertices); + glTexCoordPointer(2, GL_FLOAT, 0, texCoordinates); + glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, indices); + + // restore default GL state + glEnableClientState(GL_COLOR_ARRAY); +} + +-(void)calculateVertexPoints +{ + float width = (float)texture_.pixelsWide; + float height = (float)texture_.pixelsHigh; + float imageH = texture_.contentSizeInPixels.height; + + NSInteger numQuads = gridSize_.x * gridSize_.y; + + vertices = malloc(numQuads*12*sizeof(GLfloat)); + originalVertices = malloc(numQuads*12*sizeof(GLfloat)); + texCoordinates = malloc(numQuads*8*sizeof(GLfloat)); + indices = malloc(numQuads*6*sizeof(GLushort)); + + float *vertArray = (float*)vertices; + float *texArray = (float*)texCoordinates; + GLushort *idxArray = (GLushort *)indices; + + int x, y; + + for( x = 0; x < gridSize_.x; x++ ) + { + for( y = 0; y < gridSize_.y; y++ ) + { + float x1 = x * step_.x; + float x2 = x1 + step_.x; + float y1 = y * step_.y; + float y2 = y1 + step_.y; + + *vertArray++ = x1; + *vertArray++ = y1; + *vertArray++ = 0; + *vertArray++ = x2; + *vertArray++ = y1; + *vertArray++ = 0; + *vertArray++ = x1; + *vertArray++ = y2; + *vertArray++ = 0; + *vertArray++ = x2; + *vertArray++ = y2; + *vertArray++ = 0; + + float newY1 = y1; + float newY2 = y2; + + if( isTextureFlipped_ ) { + newY1 = imageH - y1; + newY2 = imageH - y2; + } + + *texArray++ = x1 / width; + *texArray++ = newY1 / height; + *texArray++ = x2 / width; + *texArray++ = newY1 / height; + *texArray++ = x1 / width; + *texArray++ = newY2 / height; + *texArray++ = x2 / width; + *texArray++ = newY2 / height; + } + } + + for( x = 0; x < numQuads; x++) + { + idxArray[x*6+0] = x*4+0; + idxArray[x*6+1] = x*4+1; + idxArray[x*6+2] = x*4+2; + + idxArray[x*6+3] = x*4+1; + idxArray[x*6+4] = x*4+2; + idxArray[x*6+5] = x*4+3; + } + + memcpy(originalVertices, vertices, numQuads*12*sizeof(GLfloat)); +} + +-(void)setTile:(ccGridSize)pos coords:(ccQuad3)coords +{ + NSInteger idx = (gridSize_.y * pos.x + pos.y) * 4 * 3; + float *vertArray = (float*)vertices; + memcpy(&vertArray[idx], &coords, sizeof(ccQuad3)); +} + +-(ccQuad3)originalTile:(ccGridSize)pos +{ + NSInteger idx = (gridSize_.y * pos.x + pos.y) * 4 * 3; + float *vertArray = (float*)originalVertices; + + ccQuad3 ret; + memcpy(&ret, &vertArray[idx], sizeof(ccQuad3)); + + return ret; +} + +-(ccQuad3)tile:(ccGridSize)pos +{ + NSInteger idx = (gridSize_.y * pos.x + pos.y) * 4 * 3; + float *vertArray = (float*)vertices; + + ccQuad3 ret; + memcpy(&ret, &vertArray[idx], sizeof(ccQuad3)); + + return ret; +} + +-(void)reuse +{ + if ( reuseGrid_ > 0 ) + { + NSInteger numQuads = gridSize_.x * gridSize_.y; + + memcpy(originalVertices, vertices, numQuads*12*sizeof(GLfloat)); + reuseGrid_--; + } +} + +@end diff --git a/libs/cocos2d/CCLabelAtlas.h b/libs/cocos2d/CCLabelAtlas.h new file mode 100644 index 0000000..d30fb38 --- /dev/null +++ b/libs/cocos2d/CCLabelAtlas.h @@ -0,0 +1,61 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CCAtlasNode.h" +#import "CCTextureAtlas.h" + +/** CCLabelAtlas is a subclass of CCAtlasNode. + + It can be as a replacement of CCLabel since it is MUCH faster. + + CCLabelAtlas versus CCLabel: + - CCLabelAtlas is MUCH faster than CCLabel + - CCLabelAtlas "characters" have a fixed height and width + - CCLabelAtlas "characters" can be anything you want since they are taken from an image file + + A more flexible class is CCBitmapFontAtlas. It supports variable width characters and it also has a nice editor. + */ +@interface CCLabelAtlas : CCAtlasNode +{ + // string to render + NSString *string_; + + // the first char in the charmap + char mapStartChar; +} + + +/** creates the CCLabelAtlas with a string, a char map file(the atlas), the width and height of each element in points and the starting char of the atlas */ ++(id) labelWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c; + +/** creates the CCLabelAtlas with a string, a char map file(the atlas), the width and height of each element in points and the starting char of the atlas. + @deprecated Will be removed in 1.0.1. Use "labelWithString:" instead + */ ++(id) labelAtlasWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c DEPRECATED_ATTRIBUTE; + +/** initializes the CCLabelAtlas with a string, a char map file(the atlas), the width and height in points of each element and the starting char of the atlas */ +-(id) initWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c; +@end diff --git a/libs/cocos2d/CCLabelAtlas.m b/libs/cocos2d/CCLabelAtlas.m new file mode 100644 index 0000000..33b7aa5 --- /dev/null +++ b/libs/cocos2d/CCLabelAtlas.m @@ -0,0 +1,189 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "ccConfig.h" +#import "ccMacros.h" +#import "CCDrawingPrimitives.h" +#import "CCLabelAtlas.h" +#import "Support/CGPointExtension.h" + + + +@implementation CCLabelAtlas + +#pragma mark CCLabelAtlas - Creation & Init ++(id) labelWithString:(NSString*)string charMapFile:(NSString*)charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c +{ + return [[[self alloc] initWithString:string charMapFile:charmapfile itemWidth:w itemHeight:h startCharMap:c] autorelease]; +} + +// XXX DEPRECATED. Remove it in 1.0.1 ++(id) labelAtlasWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c +{ + return [self labelWithString:string charMapFile:charmapfile itemWidth:w itemHeight:h startCharMap:c]; +} + + +-(id) initWithString:(NSString*) theString charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c +{ + + if ((self=[super initWithTileFile:charmapfile tileWidth:w tileHeight:h itemsToRender:[theString length] ]) ) { + + mapStartChar = c; + [self setString: theString]; + } + + return self; +} + +-(void) dealloc +{ + [string_ release]; + + [super dealloc]; +} + +#pragma mark CCLabelAtlas - Atlas generation + +-(void) updateAtlasValues +{ + NSInteger n = [string_ length]; + + ccV3F_C4B_T2F_Quad quad; + + const char *s = [string_ UTF8String]; + + CCTexture2D *texture = [textureAtlas_ texture]; + float textureWide = [texture pixelsWide]; + float textureHigh = [texture pixelsHigh]; + + for( NSUInteger i=0; i textureAtlas_.totalQuads ) + [textureAtlas_ resizeCapacity: newString.length]; + + [string_ release]; + string_ = [newString copy]; + [self updateAtlasValues]; + + CGSize s; + s.width = [string_ length] * itemWidth_; + s.height = itemHeight_; + [self setContentSizeInPixels:s]; +} + +-(NSString*) string +{ + return string_; +} + +#pragma mark CCLabelAtlas - draw + +// XXX: overriding draw from AtlasNode +- (void) draw +{ + // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY + // Unneeded states: GL_COLOR_ARRAY + glDisableClientState(GL_COLOR_ARRAY); + + glColor4ub( color_.r, color_.g, color_.b, opacity_); + + BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST; + if( newBlend ) + glBlendFunc( blendFunc_.src, blendFunc_.dst ); + + [textureAtlas_ drawNumberOfQuads:string_.length fromIndex:0]; + + if( newBlend ) + glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + + // is this chepear than saving/restoring color state ? + // XXX: There is no need to restore the color to (255,255,255,255). Objects should use the color + // XXX: that they need +// glColor4ub( 255, 255, 255, 255); + + // Restore Default GL state. Enable GL_COLOR_ARRAY + glEnableClientState(GL_COLOR_ARRAY); + + +#if CC_LABELATLAS_DEBUG_DRAW + CGSize s = [self contentSize]; + CGPoint vertices[4]={ + ccp(0,0),ccp(s.width,0), + ccp(s.width,s.height),ccp(0,s.height), + }; + ccDrawPoly(vertices, 4, YES); +#endif // CC_LABELATLAS_DEBUG_DRAW + +} +@end diff --git a/libs/cocos2d/CCLabelBMFont.h b/libs/cocos2d/CCLabelBMFont.h new file mode 100644 index 0000000..38b32dc --- /dev/null +++ b/libs/cocos2d/CCLabelBMFont.h @@ -0,0 +1,189 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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. + * + * Portions of this code are based and inspired on: + * http://www.71squared.co.uk/2009/04/iphone-game-programming-tutorial-4-bitmap-font-class + * by Michael Daley + + * Use any of these editors to generate bitmap font atlas: + * http://www.n4te.com/hiero/hiero.jnlp + * http://slick.cokeandcode.com/demos/hiero.jnlp + * http://www.angelcode.com/products/bmfont/ + * + */ + +#import "CCSpriteBatchNode.h" +#import "Support/uthash.h" + +struct _KerningHashElement; + +/** @struct ccBMFontDef + BMFont definition + */ +typedef struct _BMFontDef { + //! ID of the character + unsigned int charID; + //! origin and size of the font + CGRect rect; + //! The X amount the image should be offset when drawing the image (in pixels) + int xOffset; + //! The Y amount the image should be offset when drawing the image (in pixels) + int yOffset; + //! The amount to move the current position after drawing the character (in pixels) + int xAdvance; +} ccBMFontDef; + +/** @struct ccBMFontPadding + BMFont padding + @since v0.8.2 + */ +typedef struct _BMFontPadding { + /// padding left + int left; + /// padding top + int top; + /// padding right + int right; + /// padding bottom + int bottom; +} ccBMFontPadding; + +enum { + // how many characters are supported + kCCBMFontMaxChars = 2048, //256, +}; + +/** CCBMFontConfiguration has parsed configuration of the the .fnt file + @since v0.8 + */ +@interface CCBMFontConfiguration : NSObject +{ +// XXX: Creating a public interface so that the bitmapFontArray[] is accesible +@public + // The characters building up the font + ccBMFontDef BMFontArray_[kCCBMFontMaxChars]; + + // FNTConfig: Common Height + NSUInteger commonHeight_; + + // Padding + ccBMFontPadding padding_; + + // atlas name + NSString *atlasName_; + + // values for kerning + struct _KerningHashElement *kerningDictionary_; +} + +/** allocates a CCBMFontConfiguration with a FNT file */ ++(id) configurationWithFNTFile:(NSString*)FNTfile; +/** initializes a CCBMFontConfiguration with a FNT file */ +-(id) initWithFNTfile:(NSString*)FNTfile; +@end + + +/** CCLabelBMFont is a subclass of CCSpriteBatchNode + + Features: + - Treats each character like a CCSprite. This means that each individual character can be: + - rotated + - scaled + - translated + - tinted + - chage the opacity + - It can be used as part of a menu item. + - anchorPoint can be used to align the "label" + - Supports AngelCode text format + + Limitations: + - All inner characters are using an anchorPoint of (0.5f, 0.5f) and it is not recommend to change it + because it might affect the rendering + + CCLabelBMFont implements the protocol CCLabelProtocol, like CCLabel and CCLabelAtlas. + CCLabelBMFont has the flexibility of CCLabel, the speed of CCLabelAtlas and all the features of CCSprite. + If in doubt, use CCLabelBMFont instead of CCLabelAtlas / CCLabel. + + Supported editors: + - http://www.n4te.com/hiero/hiero.jnlp + - http://slick.cokeandcode.com/demos/hiero.jnlp + - http://www.angelcode.com/products/bmfont/ + + @since v0.8 + */ + +@interface CCLabelBMFont : CCSpriteBatchNode +{ + // string to render + NSString *string_; + + CCBMFontConfiguration *configuration_; + + // texture RGBA + GLubyte opacity_; + ccColor3B color_; + BOOL opacityModifyRGB_; +} + +/** Purges the cached data. + Removes from memory the cached configurations and the atlas name dictionary. + @since v0.99.3 + */ ++(void) purgeCachedData; + +/** conforms to CCRGBAProtocol protocol */ +@property (nonatomic,readwrite) GLubyte opacity; +/** conforms to CCRGBAProtocol protocol */ +@property (nonatomic,readwrite) ccColor3B color; + + +/** creates a BMFont label with an initial string and the FNT file */ ++(id) labelWithString:(NSString*)string fntFile:(NSString*)fntFile; + +/** creates a BMFont label with an initial string and the FNT file + @deprecated Will be removed in 1.0.1. Use "labelWithString" instead. + */ ++(id) bitmapFontAtlasWithString:(NSString*)string fntFile:(NSString*)fntFile DEPRECATED_ATTRIBUTE; + +/** init a BMFont label with an initial string and the FNT file */ +-(id) initWithString:(NSString*)string fntFile:(NSString*)fntFile; + +/** updates the font chars based on the string to render */ +-(void) createFontChars; +@end + +/** Free function that parses a FNT file a place it on the cache +*/ +CCBMFontConfiguration * FNTConfigLoadFile( NSString *file ); +/** Purges the FNT config cache + */ +void FNTConfigRemoveCache( void ); + + + +/** CCBitmapFontAtlas + @deprecated Use CCLabelBMFont instead. Will be removed 1.0.1 + */ +DEPRECATED_ATTRIBUTE @interface CCBitmapFontAtlas : CCLabelBMFont +@end + diff --git a/libs/cocos2d/CCLabelBMFont.m b/libs/cocos2d/CCLabelBMFont.m new file mode 100644 index 0000000..22c5759 --- /dev/null +++ b/libs/cocos2d/CCLabelBMFont.m @@ -0,0 +1,672 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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. + * + * Portions of this code are based and inspired on: + * http://www.71squared.co.uk/2009/04/iphone-game-programming-tutorial-4-bitmap-font-class + * by Michael Daley + * + * + * Use any of these editors to generate bitmap font atlas: + * http://www.n4te.com/hiero/hiero.jnlp + * http://slick.cokeandcode.com/demos/hiero.jnlp + * http://www.angelcode.com/products/bmfont/ + */ + +#import "ccConfig.h" +#import "CCLabelBMFont.h" +#import "CCSprite.h" +#import "CCDrawingPrimitives.h" +#import "CCConfiguration.h" +#import "Support/CCFileUtils.h" +#import "Support/CGPointExtension.h" +#import "Support/uthash.h" + +#pragma mark - +#pragma mark FNTConfig Cache - free functions + +NSMutableDictionary *configurations = nil; +CCBMFontConfiguration* FNTConfigLoadFile( NSString *fntFile) +{ + CCBMFontConfiguration *ret = nil; + + if( configurations == nil ) + configurations = [[NSMutableDictionary dictionaryWithCapacity:3] retain]; + + ret = [configurations objectForKey:fntFile]; + if( ret == nil ) { + ret = [CCBMFontConfiguration configurationWithFNTFile:fntFile]; + [configurations setObject:ret forKey:fntFile]; + } + + return ret; +} + +void FNTConfigRemoveCache( void ) +{ + [configurations removeAllObjects]; +} + +#pragma mark - Hash Element + +// Equal function for targetSet. +typedef struct _KerningHashElement +{ + int key; // key for the hash. 16-bit for 1st element, 16-bit for 2nd element + int amount; + UT_hash_handle hh; +} tKerningHashElement; + +#pragma mark - +#pragma mark BitmapFontConfiguration + + +@interface CCBMFontConfiguration (Private) +-(void) parseConfigFile:(NSString*)controlFile; +-(void) parseCharacterDefinition:(NSString*)line charDef:(ccBMFontDef*)characterDefinition; +-(void) parseInfoArguments:(NSString*)line; +-(void) parseCommonArguments:(NSString*)line; +-(void) parseImageFileName:(NSString*)line fntFile:(NSString*)fntFile; +-(void) parseKerningCapacity:(NSString*)line; +-(void) parseKerningEntry:(NSString*)line; +-(void) purgeKerningDictionary; +@end + +@implementation CCBMFontConfiguration + ++(id) configurationWithFNTFile:(NSString*)FNTfile +{ + return [[[self alloc] initWithFNTfile:FNTfile] autorelease]; +} + +-(id) initWithFNTfile:(NSString*)fntFile +{ + if((self=[super init])) { + + kerningDictionary_ = NULL; + + [self parseConfigFile:fntFile]; + } + return self; +} + +- (void) dealloc +{ + CCLOGINFO( @"cocos2d: deallocing %@", self); + [self purgeKerningDictionary]; + [atlasName_ release]; + [super dealloc]; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | Kernings:%d | Image = %@>", [self class], self, + HASH_COUNT(kerningDictionary_), + atlasName_]; +} + + +-(void) purgeKerningDictionary +{ + tKerningHashElement *current; + + while(kerningDictionary_) { + current = kerningDictionary_; + HASH_DEL(kerningDictionary_,current); + free(current); + } +} + +- (void)parseConfigFile:(NSString*)fntFile +{ + NSString *fullpath = [CCFileUtils fullPathFromRelativePath:fntFile]; + NSError *error; + NSString *contents = [NSString stringWithContentsOfFile:fullpath encoding:NSUTF8StringEncoding error:&error]; + + NSAssert1( contents, @"cocos2d: Error parsing FNTfile: %@", error); + + + // Move all lines in the string, which are denoted by \n, into an array + NSArray *lines = [[NSArray alloc] initWithArray:[contents componentsSeparatedByString:@"\n"]]; + + // Create an enumerator which we can use to move through the lines read from the control file + NSEnumerator *nse = [lines objectEnumerator]; + + // Create a holder for each line we are going to work with + NSString *line; + + // Loop through all the lines in the lines array processing each one + while( (line = [nse nextObject]) ) { + // parse spacing / padding + if([line hasPrefix:@"info face"]) { + // XXX: info parsing is incomplete + // Not needed for the Hiero editors, but needed for the AngelCode editor +// [self parseInfoArguments:line]; + } + // Check to see if the start of the line is something we are interested in + else if([line hasPrefix:@"common lineHeight"]) { + [self parseCommonArguments:line]; + } + else if([line hasPrefix:@"page id"]) { + [self parseImageFileName:line fntFile:fntFile]; + } + else if([line hasPrefix:@"chars c"]) { + // Ignore this line + } + else if([line hasPrefix:@"char"]) { + // Parse the current line and create a new CharDef + ccBMFontDef characterDefinition; + [self parseCharacterDefinition:line charDef:&characterDefinition]; + + // Add the CharDef returned to the charArray + BMFontArray_[ characterDefinition.charID ] = characterDefinition; + } + else if([line hasPrefix:@"kernings count"]) { + [self parseKerningCapacity:line]; + } + else if([line hasPrefix:@"kerning first"]) { + [self parseKerningEntry:line]; + } + } + // Finished with lines so release it + [lines release]; +} + +-(void) parseImageFileName:(NSString*)line fntFile:(NSString*)fntFile +{ + NSString *propertyValue = nil; + + // Break the values for this line up using = + NSArray *values = [line componentsSeparatedByString:@"="]; + + // Get the enumerator for the array of components which has been created + NSEnumerator *nse = [values objectEnumerator]; + + // We need to move past the first entry in the array before we start assigning values + [nse nextObject]; + + // page ID. Sanity check + propertyValue = [nse nextObject]; + NSAssert( [propertyValue intValue] == 0, @"XXX: BitmapFontAtlas only supports 1 page"); + + // file + propertyValue = [nse nextObject]; + NSArray *array = [propertyValue componentsSeparatedByString:@"\""]; + propertyValue = [array objectAtIndex:1]; + NSAssert(propertyValue,@"BitmapFontAtlas file could not be found"); + + // Supports subdirectories + NSString *dir = [fntFile stringByDeletingLastPathComponent]; + atlasName_ = [dir stringByAppendingPathComponent:propertyValue]; + + [atlasName_ retain]; +} + +-(void) parseInfoArguments:(NSString*)line +{ + // + // possible lines to parse: + // info face="Script" size=32 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=1,4,3,2 spacing=0,0 outline=0 + // info face="Cracked" size=36 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 + // + NSArray *values = [line componentsSeparatedByString:@"="]; + NSEnumerator *nse = [values objectEnumerator]; + NSString *propertyValue = nil; + + // We need to move past the first entry in the array before we start assigning values + [nse nextObject]; + + // face (ignore) + [nse nextObject]; + + // size (ignore) + [nse nextObject]; + + // bold (ignore) + [nse nextObject]; + + // italic (ignore) + [nse nextObject]; + + // charset (ignore) + [nse nextObject]; + + // unicode (ignore) + [nse nextObject]; + + // strechH (ignore) + [nse nextObject]; + + // smooth (ignore) + [nse nextObject]; + + // aa (ignore) + [nse nextObject]; + + // padding (ignore) + propertyValue = [nse nextObject]; + { + + NSArray *paddingValues = [propertyValue componentsSeparatedByString:@","]; + NSEnumerator *paddingEnum = [paddingValues objectEnumerator]; + // padding top + propertyValue = [paddingEnum nextObject]; + padding_.top = [propertyValue intValue]; + + // padding right + propertyValue = [paddingEnum nextObject]; + padding_.right = [propertyValue intValue]; + + // padding bottom + propertyValue = [paddingEnum nextObject]; + padding_.bottom = [propertyValue intValue]; + + // padding left + propertyValue = [paddingEnum nextObject]; + padding_.left = [propertyValue intValue]; + + CCLOG(@"cocos2d: padding: %d,%d,%d,%d", padding_.left, padding_.top, padding_.right, padding_.bottom); + } + + // spacing (ignore) + [nse nextObject]; +} + +-(void) parseCommonArguments:(NSString*)line +{ + // + // line to parse: + // common lineHeight=104 base=26 scaleW=1024 scaleH=512 pages=1 packed=0 + // + NSArray *values = [line componentsSeparatedByString:@"="]; + NSEnumerator *nse = [values objectEnumerator]; + NSString *propertyValue = nil; + + // We need to move past the first entry in the array before we start assigning values + [nse nextObject]; + + // Character ID + propertyValue = [nse nextObject]; + commonHeight_ = [propertyValue intValue]; + + // base (ignore) + [nse nextObject]; + + + // scaleW. sanity check + propertyValue = [nse nextObject]; + NSAssert( [propertyValue intValue] <= [[CCConfiguration sharedConfiguration] maxTextureSize], @"CCLabelBMFont: page can't be larger than supported"); + + // scaleH. sanity check + propertyValue = [nse nextObject]; + NSAssert( [propertyValue intValue] <= [[CCConfiguration sharedConfiguration] maxTextureSize], @"CCLabelBMFont: page can't be larger than supported"); + + // pages. sanity check + propertyValue = [nse nextObject]; + NSAssert( [propertyValue intValue] == 1, @"CCBitfontAtlas: only supports 1 page"); + + // packed (ignore) What does this mean ?? +} +- (void)parseCharacterDefinition:(NSString*)line charDef:(ccBMFontDef*)characterDefinition +{ + // Break the values for this line up using = + NSArray *values = [line componentsSeparatedByString:@"="]; + NSEnumerator *nse = [values objectEnumerator]; + NSString *propertyValue; + + // We need to move past the first entry in the array before we start assigning values + [nse nextObject]; + + // Character ID + propertyValue = [nse nextObject]; + propertyValue = [propertyValue substringToIndex: [propertyValue rangeOfString: @" "].location]; + characterDefinition->charID = [propertyValue intValue]; + NSAssert(characterDefinition->charID < kCCBMFontMaxChars, @"BitmpaFontAtlas: CharID bigger than supported"); + + // Character x + propertyValue = [nse nextObject]; + characterDefinition->rect.origin.x = [propertyValue intValue]; + // Character y + propertyValue = [nse nextObject]; + characterDefinition->rect.origin.y = [propertyValue intValue]; + // Character width + propertyValue = [nse nextObject]; + characterDefinition->rect.size.width = [propertyValue intValue]; + // Character height + propertyValue = [nse nextObject]; + characterDefinition->rect.size.height = [propertyValue intValue]; + // Character xoffset + propertyValue = [nse nextObject]; + characterDefinition->xOffset = [propertyValue intValue]; + // Character yoffset + propertyValue = [nse nextObject]; + characterDefinition->yOffset = [propertyValue intValue]; + // Character xadvance + propertyValue = [nse nextObject]; + characterDefinition->xAdvance = [propertyValue intValue]; +} + +-(void) parseKerningCapacity:(NSString*) line +{ + // When using uthash there is not need to parse the capacity. + +// NSAssert(!kerningDictionary, @"dictionary already initialized"); +// +// // Break the values for this line up using = +// NSArray *values = [line componentsSeparatedByString:@"="]; +// NSEnumerator *nse = [values objectEnumerator]; +// NSString *propertyValue; +// +// // We need to move past the first entry in the array before we start assigning values +// [nse nextObject]; +// +// // count +// propertyValue = [nse nextObject]; +// int capacity = [propertyValue intValue]; +// +// if( capacity != -1 ) +// kerningDictionary = ccHashSetNew(capacity, targetSetEql); +} + +-(void) parseKerningEntry:(NSString*) line +{ + NSArray *values = [line componentsSeparatedByString:@"="]; + NSEnumerator *nse = [values objectEnumerator]; + NSString *propertyValue; + + // We need to move past the first entry in the array before we start assigning values + [nse nextObject]; + + // first + propertyValue = [nse nextObject]; + int first = [propertyValue intValue]; + + // second + propertyValue = [nse nextObject]; + int second = [propertyValue intValue]; + + // second + propertyValue = [nse nextObject]; + int amount = [propertyValue intValue]; + + tKerningHashElement *element = calloc( sizeof( *element ), 1 ); + element->amount = amount; + element->key = (first<<16) | (second&0xffff); + HASH_ADD_INT(kerningDictionary_,key, element); +} + +@end + +#pragma mark - +#pragma mark CCLabelBMFont + +@interface CCLabelBMFont (Private) +-(NSString*) atlasNameFromFntFile:(NSString*)fntFile; + +-(int) kerningAmountForFirst:(unichar)first second:(unichar)second; + +@end + +@implementation CCLabelBMFont + +@synthesize opacity = opacity_, color = color_; + +#pragma mark BitmapFontAtlas - Purge Cache ++(void) purgeCachedData +{ + FNTConfigRemoveCache(); +} + +#pragma mark BitmapFontAtlas - Creation & Init + ++(id) labelWithString:(NSString *)string fntFile:(NSString *)fntFile +{ + return [[[self alloc] initWithString:string fntFile:fntFile] autorelease]; +} + +// XXX - deprecated - Will be removed in 1.0.1 ++(id) bitmapFontAtlasWithString:(NSString*)string fntFile:(NSString*)fntFile +{ + return [self labelWithString:string fntFile:fntFile]; +} + +-(id) initWithString:(NSString*)theString fntFile:(NSString*)fntFile +{ + + [configuration_ release]; // allow re-init + + configuration_ = FNTConfigLoadFile(fntFile); + [configuration_ retain]; + + NSAssert( configuration_, @"Error creating config for BitmapFontAtlas"); + + + if ((self=[super initWithFile:configuration_->atlasName_ capacity:[theString length]])) { + + opacity_ = 255; + color_ = ccWHITE; + + contentSize_ = CGSizeZero; + + opacityModifyRGB_ = [[textureAtlas_ texture] hasPremultipliedAlpha]; + + anchorPoint_ = ccp(0.5f, 0.5f); + + [self setString:theString]; + } + + return self; +} + +-(void) dealloc +{ + [string_ release]; + [configuration_ release]; + [super dealloc]; +} + +#pragma mark BitmapFontAtlas - Atlas generation + +-(int) kerningAmountForFirst:(unichar)first second:(unichar)second +{ + int ret = 0; + unsigned int key = (first<<16) | (second & 0xffff); + + if( configuration_->kerningDictionary_ ) { + tKerningHashElement *element = NULL; + HASH_FIND_INT(configuration_->kerningDictionary_, &key, element); + if(element) + ret = element->amount; + } + + return ret; +} + +-(void) createFontChars +{ + NSInteger nextFontPositionX = 0; + NSInteger nextFontPositionY = 0; + unichar prev = -1; + NSInteger kerningAmount = 0; + + CGSize tmpSize = CGSizeZero; + + NSInteger longestLine = 0; + NSUInteger totalHeight = 0; + + NSUInteger quantityOfLines = 1; + + NSUInteger stringLen = [string_ length]; + if( ! stringLen ) + return; + + // quantity of lines NEEDS to be calculated before parsing the lines, + // since the Y position needs to be calcualted before hand + for(NSUInteger i=0; i < stringLen-1;i++) { + unichar c = [string_ characterAtIndex:i]; + if( c=='\n') + quantityOfLines++; + } + + totalHeight = configuration_->commonHeight_ * quantityOfLines; + nextFontPositionY = -(configuration_->commonHeight_ - configuration_->commonHeight_*quantityOfLines); + + for(NSUInteger i=0; icommonHeight_; + continue; + } + + kerningAmount = [self kerningAmountForFirst:prev second:c]; + + ccBMFontDef fontDef = configuration_->BMFontArray_[c]; + + CGRect rect = fontDef.rect; + + CCSprite *fontChar; + + fontChar = (CCSprite*) [self getChildByTag:i]; + if( ! fontChar ) { + fontChar = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect]; + [self addChild:fontChar z:0 tag:i]; + [fontChar release]; + } + else { + // reusing fonts + [fontChar setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size]; + + // restore to default in case they were modified + fontChar.visible = YES; + fontChar.opacity = 255; + } + + float yOffset = configuration_->commonHeight_ - fontDef.yOffset; + fontChar.positionInPixels = ccp( (float)nextFontPositionX + fontDef.xOffset + fontDef.rect.size.width*0.5f + kerningAmount, + (float)nextFontPositionY + yOffset - rect.size.height*0.5f ); + + // update kerning + nextFontPositionX += configuration_->BMFontArray_[c].xAdvance + kerningAmount; + prev = c; + + // Apply label properties + [fontChar setOpacityModifyRGB:opacityModifyRGB_]; + // Color MUST be set before opacity, since opacity might change color if OpacityModifyRGB is on + [fontChar setColor:color_]; + + // only apply opacity if it is different than 255 ) + // to prevent modifying the color too (issue #610) + if( opacity_ != 255 ) + [fontChar setOpacity: opacity_]; + + if (longestLine < nextFontPositionX) + longestLine = nextFontPositionX; + } + + tmpSize.width = longestLine; + tmpSize.height = totalHeight; + + [self setContentSizeInPixels:tmpSize]; +} + +#pragma mark BitmapFontAtlas - CCLabelProtocol protocol +- (void) setString:(NSString*) newString +{ + [string_ release]; + string_ = [newString copy]; + + CCNode *child; + CCARRAY_FOREACH(children_, child) + child.visible = NO; + + [self createFontChars]; +} + +-(NSString*) string +{ + return string_; +} + +-(void) setCString:(char*)label +{ + [self setString:[NSString stringWithUTF8String:label]]; +} + +#pragma mark BitmapFontAtlas - CCRGBAProtocol protocol + +-(void) setColor:(ccColor3B)color +{ + color_ = color; + + CCSprite *child; + CCARRAY_FOREACH(children_, child) + [child setColor:color_]; +} + +-(void) setOpacity:(GLubyte)opacity +{ + opacity_ = opacity; + + id child; + CCARRAY_FOREACH(children_, child) + [child setOpacity:opacity_]; +} +-(void) setOpacityModifyRGB:(BOOL)modify +{ + opacityModifyRGB_ = modify; + + id child; + CCARRAY_FOREACH(children_, child) + [child setOpacityModifyRGB:modify]; +} + +-(BOOL) doesOpacityModifyRGB +{ + return opacityModifyRGB_; +} + +#pragma mark BitmapFontAtlas - AnchorPoint +-(void) setAnchorPoint:(CGPoint)point +{ + if( ! CGPointEqualToPoint(point, anchorPoint_) ) { + [super setAnchorPoint:point]; + [self createFontChars]; + } +} + +#pragma mark BitmapFontAtlas - Debug draw +#if CC_LABELBMFONT_DEBUG_DRAW +-(void) draw +{ + [super draw]; + CGSize s = [self contentSize]; + CGPoint vertices[4]={ + ccp(0,0),ccp(s.width,0), + ccp(s.width,s.height),ccp(0,s.height), + }; + ccDrawPoly(vertices, 4, YES); +} +#endif // CC_LABELBMFONT_DEBUG_DRAW +@end diff --git a/libs/cocos2d/CCLabelTTF.h b/libs/cocos2d/CCLabelTTF.h new file mode 100644 index 0000000..66cdda7 --- /dev/null +++ b/libs/cocos2d/CCLabelTTF.h @@ -0,0 +1,62 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CCTexture2D.h" +#import "CCSprite.h" +#import "Platforms/CCNS.h" + + +/** CCLabel is a subclass of CCTextureNode that knows how to render text labels + * + * All features from CCTextureNode are valid in CCLabel + * + * CCLabel objects are slow. Consider using CCLabelAtlas or CCBitmapFontAtlas instead. + */ + +@interface CCLabelTTF : CCSprite +{ + CGSize dimensions_; + CCTextAlignment alignment_; + NSString * fontName_; + CGFloat fontSize_; + NSString *string_; +} + +/** creates a CCLabel from a fontname, alignment, dimension in points and font size in points*/ ++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size; +/** creates a CCLabel from a fontname and font size in points*/ ++ (id) labelWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size; +/** initializes the CCLabel with a font name, alignment, dimension in points and font size in points */ +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size; +/** initializes the CCLabel with a font name and font size in points */ +- (id) initWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size; + +/** changes the string to render + * @warning Changing the string is as expensive as creating a new CCLabel. To obtain better performance use CCLabelAtlas + */ +- (void) setString:(NSString*)str; + +@end diff --git a/libs/cocos2d/CCLabelTTF.m b/libs/cocos2d/CCLabelTTF.m new file mode 100644 index 0000000..5760a20 --- /dev/null +++ b/libs/cocos2d/CCLabelTTF.m @@ -0,0 +1,125 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 + +#import "CCLabelTTF.h" +#import "Support/CGPointExtension.h" +#import "ccMacros.h" + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "Platforms/iOS/CCDirectorIOS.h" +#endif + +@implementation CCLabelTTF + +- (id) init +{ + NSAssert(NO, @"CCLabelTTF: Init not supported. Use initWithString"); + [self release]; + return nil; +} + ++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size +{ + return [[[self alloc] initWithString: string dimensions:dimensions alignment:alignment fontName:name fontSize:size]autorelease]; +} + ++ (id) labelWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size +{ + return [[[self alloc] initWithString: string fontName:name fontSize:size]autorelease]; +} + + +- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size +{ + if( (self=[super init]) ) { + + dimensions_ = CGSizeMake( dimensions.width * CC_CONTENT_SCALE_FACTOR(), dimensions.height * CC_CONTENT_SCALE_FACTOR() ); + alignment_ = alignment; + fontName_ = [name retain]; + fontSize_ = size * CC_CONTENT_SCALE_FACTOR(); + + [self setString:str]; + } + return self; +} + +- (id) initWithString:(NSString*)str fontName:(NSString*)name fontSize:(CGFloat)size +{ + if( (self=[super init]) ) { + + dimensions_ = CGSizeZero; + fontName_ = [name retain]; + fontSize_ = size * CC_CONTENT_SCALE_FACTOR(); + + [self setString:str]; + } + return self; +} + +- (void) setString:(NSString*)str +{ + [string_ release]; + string_ = [str copy]; + + CCTexture2D *tex; + if( CGSizeEqualToSize( dimensions_, CGSizeZero ) ) + tex = [[CCTexture2D alloc] initWithString:str + fontName:fontName_ + fontSize:fontSize_]; + else + tex = [[CCTexture2D alloc] initWithString:str + dimensions:dimensions_ + alignment:alignment_ + fontName:fontName_ + fontSize:fontSize_]; + + [self setTexture:tex]; + [tex release]; + + CGRect rect = CGRectZero; + rect.size = [texture_ contentSize]; + [self setTextureRect: rect]; +} + +-(NSString*) string +{ + return string_; +} + +- (void) dealloc +{ + [string_ release]; + [fontName_ release]; + [super dealloc]; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | FontName = %@, FontSize = %.1f>", [self class], self, fontName_, fontSize_]; +} +@end diff --git a/libs/cocos2d/CCLayer.h b/libs/cocos2d/CCLayer.h new file mode 100644 index 0000000..e995f29 --- /dev/null +++ b/libs/cocos2d/CCLayer.h @@ -0,0 +1,292 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import // Needed for UIAccelerometerDelegate +#import "Platforms/iOS/CCTouchDelegateProtocol.h" // Touches only supported on iOS +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import "Platforms/Mac/CCEventDispatcher.h" +#endif + +#import "CCProtocols.h" +#import "CCNode.h" + +#pragma mark - +#pragma mark CCLayer + +/** CCLayer is a subclass of CCNode that implements the TouchEventsDelegate protocol. + + All features from CCNode are valid, plus the following new features: + - It can receive iPhone Touches + - It can receive Accelerometer input +*/ +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +@interface CCLayer : CCNode +{ + BOOL isTouchEnabled_; + BOOL isAccelerometerEnabled_; +} +/** If isTouchEnabled, this method is called onEnter. Override it to change the + way CCLayer receives touch events. + ( Default: [[TouchDispatcher sharedDispatcher] addStandardDelegate:self priority:0] ) + Example: + -(void) registerWithTouchDispatcher + { + [[TouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES]; + } + + Valid only on iOS. Not valid on Mac. + + @since v0.8.0 + */ +-(void) registerWithTouchDispatcher; + +/** whether or not it will receive Touch events. + You can enable / disable touch events with this property. + Only the touches of this node will be affected. This "method" is not propagated to it's children. + + Valid on iOS and Mac OS X v10.6 and later. + + @since v0.8.1 + */ +@property(nonatomic,assign) BOOL isTouchEnabled; +/** whether or not it will receive Accelerometer events + You can enable / disable accelerometer events with this property. + + Valid only on iOS. Not valid on Mac. + + @since v0.8.1 + */ +@property(nonatomic,assign) BOOL isAccelerometerEnabled; + +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + + +@interface CCLayer : CCNode +{ + BOOL isMouseEnabled_; + BOOL isKeyboardEnabled_; + BOOL isTouchEnabled_; +} + +/** whether or not it will receive mouse events. + + Valind only Mac. Not valid on iOS + */ +@property (nonatomic, readwrite) BOOL isMouseEnabled; + +/** whether or not it will receive keyboard events. + + Valind only Mac. Not valid on iOS + */ +@property (nonatomic, readwrite) BOOL isKeyboardEnabled; + +/** whether or not it will receive touch events. + + Valid on iOS and Mac OS X v10.6 and later. + */ +@property (nonatomic, readwrite) BOOL isTouchEnabled; + +/** priority of the mouse event delegate. + Default 0. + Override this method to set another priority. + + Valind only Mac. Not valid on iOS + */ +-(NSInteger) mouseDelegatePriority; + +/** priority of the keyboard event delegate. + Default 0. + Override this method to set another priority. + + Valind only Mac. Not valid on iOS + */ +-(NSInteger) keyboardDelegatePriority; + +/** priority of the touch event delegate. + Default 0. + Override this method to set another priority. + + Valind only Mac. Not valid on iOS + */ +-(NSInteger) touchDelegatePriority; + +#endif // mac + + +@end + +#pragma mark - +#pragma mark CCLayerColor + +/** CCLayerColor is a subclass of CCLayer that implements the CCRGBAProtocol protocol. + + All features from CCLayer are valid, plus the following new features: + - opacity + - RGB colors + */ +@interface CCLayerColor : CCLayer +{ + GLubyte opacity_; + ccColor3B color_; + ccVertex2F squareVertices_[4]; + ccColor4B squareColors_[4]; + + ccBlendFunc blendFunc_; +} + +/** creates a CCLayer with color, width and height in Points*/ ++ (id) layerWithColor: (ccColor4B)color width:(GLfloat)w height:(GLfloat)h; +/** creates a CCLayer with color. Width and height are the window size. */ ++ (id) layerWithColor: (ccColor4B)color; + +/** initializes a CCLayer with color, width and height in Points */ +- (id) initWithColor:(ccColor4B)color width:(GLfloat)w height:(GLfloat)h; +/** initializes a CCLayer with color. Width and height are the window size. */ +- (id) initWithColor:(ccColor4B)color; + +/** change width in Points */ +-(void) changeWidth: (GLfloat)w; +/** change height in Points */ +-(void) changeHeight: (GLfloat)h; +/** change width and height in Points + @since v0.8 + */ +-(void) changeWidth:(GLfloat)w height:(GLfloat)h; + +/** Opacity: conforms to CCRGBAProtocol protocol */ +@property (nonatomic,readonly) GLubyte opacity; +/** Opacity: conforms to CCRGBAProtocol protocol */ +@property (nonatomic,readonly) ccColor3B color; +/** BlendFunction. Conforms to CCBlendProtocol protocol */ +@property (nonatomic,readwrite) ccBlendFunc blendFunc; +@end + +/** CCColorLayer + It is the same as CCLayerColor. + + @deprecated Use CCLayerColor instead. This class will be removed in v1.0.1 + */ +DEPRECATED_ATTRIBUTE @interface CCColorLayer : CCLayerColor +@end + +#pragma mark - +#pragma mark CCLayerGradient + +/** CCLayerGradient is a subclass of CCLayerColor that draws gradients across +the background. + + All features from CCLayerColor are valid, plus the following new features: + - direction + - final color + - interpolation mode + + Color is interpolated between the startColor and endColor along the given + vector (starting at the origin, ending at the terminus). If no vector is + supplied, it defaults to (0, -1) -- a fade from top to bottom. + + If 'compressedInterpolation' is disabled, you will not see either the start or end color for + non-cardinal vectors; a smooth gradient implying both end points will be still + be drawn, however. + + If ' compressedInterpolation' is enabled (default mode) you will see both the start and end colors of the gradient. + + @since v0.99.5 + */ +@interface CCLayerGradient : CCLayerColor +{ + ccColor3B endColor_; + GLubyte startOpacity_; + GLubyte endOpacity_; + CGPoint vector_; + BOOL compressedInterpolation_; +} + +/** Creates a full-screen CCLayer with a gradient between start and end. */ ++ (id) layerWithColor: (ccColor4B) start fadingTo: (ccColor4B) end; +/** Creates a full-screen CCLayer with a gradient between start and end in the direction of v. */ ++ (id) layerWithColor: (ccColor4B) start fadingTo: (ccColor4B) end alongVector: (CGPoint) v; + +/** Initializes the CCLayer with a gradient between start and end. */ +- (id) initWithColor: (ccColor4B) start fadingTo: (ccColor4B) end; +/** Initializes the CCLayer with a gradient between start and end in the direction of v. */ +- (id) initWithColor: (ccColor4B) start fadingTo: (ccColor4B) end alongVector: (CGPoint) v; + +/** The starting color. */ +@property (nonatomic, readwrite) ccColor3B startColor; +/** The ending color. */ +@property (nonatomic, readwrite) ccColor3B endColor; +/** The starting opacity. */ +@property (nonatomic, readwrite) GLubyte startOpacity; +/** The ending color. */ +@property (nonatomic, readwrite) GLubyte endOpacity; +/** The vector along which to fade color. */ +@property (nonatomic, readwrite) CGPoint vector; +/** Whether or not the interpolation will be compressed in order to display all the colors of the gradient both in canonical and non canonical vectors + Default: YES + */ +@property (nonatomic, readwrite) BOOL compressedInterpolation; + +@end + +#pragma mark - +#pragma mark CCLayerMultiplex + +/** CCLayerMultiplex is a CCLayer with the ability to multiplex it's children. + Features: + - It supports one or more children + - Only one children will be active a time + */ +@interface CCLayerMultiplex : CCLayer +{ + unsigned int enabledLayer_; + NSMutableArray *layers_; +} + +/** creates a CCMultiplexLayer with one or more layers using a variable argument list. */ ++(id) layerWithLayers: (CCLayer*) layer, ... NS_REQUIRES_NIL_TERMINATION; +/** initializes a MultiplexLayer with one or more layers using a variable argument list. */ +-(id) initWithLayers: (CCLayer*) layer vaList:(va_list) params; +/** switches to a certain layer indexed by n. + The current (old) layer will be removed from it's parent with 'cleanup:YES'. + */ +-(void) switchTo: (unsigned int) n; +/** release the current layer and switches to another layer indexed by n. + The current (old) layer will be removed from it's parent with 'cleanup:YES'. + */ +-(void) switchToAndReleaseMe: (unsigned int) n; +@end + +/** CCMultiplexLayer + It is the same as CCLayerMultiplex. + + @deprecated Use CCLayerMultiplex instead. This class will be removed in v1.0.1 + */ +DEPRECATED_ATTRIBUTE @interface CCMultiplexLayer : CCLayerMultiplex +@end + diff --git a/libs/cocos2d/CCLayer.m b/libs/cocos2d/CCLayer.m new file mode 100644 index 0000000..c0e3f98 --- /dev/null +++ b/libs/cocos2d/CCLayer.m @@ -0,0 +1,618 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 + +#import "Platforms/CCGL.h" + +#import "CCLayer.h" +#import "CCDirector.h" +#import "ccMacros.h" +#import "Support/CGPointExtension.h" + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "Platforms/iOS/CCTouchDispatcher.h" +#import "Platforms/iOS/CCDirectorIOS.h" +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import "Platforms/Mac/CCEventDispatcher.h" +#endif + +#pragma mark - +#pragma mark Layer + +@implementation CCLayer + +#pragma mark Layer - Init +-(id) init +{ + if( (self=[super init]) ) { + + CGSize s = [[CCDirector sharedDirector] winSize]; + anchorPoint_ = ccp(0.5f, 0.5f); + [self setContentSize:s]; + self.isRelativeAnchorPoint = NO; + + isTouchEnabled_ = NO; + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + isAccelerometerEnabled_ = NO; +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + isMouseEnabled_ = NO; + isKeyboardEnabled_ = NO; +#endif + } + + return self; +} + +#pragma mark Layer - Touch and Accelerometer related + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +-(void) registerWithTouchDispatcher +{ + [[CCTouchDispatcher sharedDispatcher] addStandardDelegate:self priority:0]; +} + +-(BOOL) isAccelerometerEnabled +{ + return isAccelerometerEnabled_; +} + +-(void) setIsAccelerometerEnabled:(BOOL)enabled +{ + if( enabled != isAccelerometerEnabled_ ) { + isAccelerometerEnabled_ = enabled; + if( isRunning_ ) { + if( enabled ) + [[UIAccelerometer sharedAccelerometer] setDelegate:self]; + else + [[UIAccelerometer sharedAccelerometer] setDelegate:nil]; + } + } +} + +-(BOOL) isTouchEnabled +{ + return isTouchEnabled_; +} + +-(void) setIsTouchEnabled:(BOOL)enabled +{ + if( isTouchEnabled_ != enabled ) { + isTouchEnabled_ = enabled; + if( isRunning_ ) { + if( enabled ) + [self registerWithTouchDispatcher]; + else + [[CCTouchDispatcher sharedDispatcher] removeDelegate:self]; + } + } +} + +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +#pragma mark CCLayer - Mouse, Keyboard & Touch events + +-(NSInteger) mouseDelegatePriority +{ + return 0; +} + +-(BOOL) isMouseEnabled +{ + return isMouseEnabled_; +} + +-(void) setIsMouseEnabled:(BOOL)enabled +{ + if( isMouseEnabled_ != enabled ) { + isMouseEnabled_ = enabled; + + if( isRunning_ ) { + if( enabled ) + [[CCEventDispatcher sharedDispatcher] addMouseDelegate:self priority:[self mouseDelegatePriority]]; + else + [[CCEventDispatcher sharedDispatcher] removeMouseDelegate:self]; + } + } +} + +-(NSInteger) keyboardDelegatePriority +{ + return 0; +} + +-(BOOL) isKeyboardEnabled +{ + return isKeyboardEnabled_; +} + +-(void) setIsKeyboardEnabled:(BOOL)enabled +{ + if( isKeyboardEnabled_ != enabled ) { + isKeyboardEnabled_ = enabled; + + if( isRunning_ ) { + if( enabled ) + [[CCEventDispatcher sharedDispatcher] addKeyboardDelegate:self priority:[self keyboardDelegatePriority] ]; + else + [[CCEventDispatcher sharedDispatcher] removeKeyboardDelegate:self]; + } + } +} + +-(NSInteger) touchDelegatePriority +{ + return 0; +} + +-(BOOL) isTouchEnabled +{ + return isTouchEnabled_; +} + +-(void) setIsTouchEnabled:(BOOL)enabled +{ + if( isTouchEnabled_ != enabled ) { + isTouchEnabled_ = enabled; + if( isRunning_ ) { + if( enabled ) + [[CCEventDispatcher sharedDispatcher] addTouchDelegate:self priority:[self touchDelegatePriority]]; + else + [[CCEventDispatcher sharedDispatcher] removeTouchDelegate:self]; + } + } +} + + +#endif // Mac + + +#pragma mark Layer - Callbacks +-(void) onEnter +{ +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + // register 'parent' nodes first + // since events are propagated in reverse order + if (isTouchEnabled_) + [self registerWithTouchDispatcher]; + +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + if( isMouseEnabled_ ) + [[CCEventDispatcher sharedDispatcher] addMouseDelegate:self priority:[self mouseDelegatePriority]]; + + if( isKeyboardEnabled_) + [[CCEventDispatcher sharedDispatcher] addKeyboardDelegate:self priority:[self keyboardDelegatePriority]]; + + if( isTouchEnabled_) + [[CCEventDispatcher sharedDispatcher] addTouchDelegate:self priority:[self touchDelegatePriority]]; + +#endif + + // then iterate over all the children + [super onEnter]; +} + +// issue #624. +// Can't register mouse, touches here because of #issue #1018, and #1021 +-(void) onEnterTransitionDidFinish +{ +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + if( isAccelerometerEnabled_ ) + [[UIAccelerometer sharedAccelerometer] setDelegate:self]; +#endif + + [super onEnterTransitionDidFinish]; +} + + +-(void) onExit +{ +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + if( isTouchEnabled_ ) + [[CCTouchDispatcher sharedDispatcher] removeDelegate:self]; + + if( isAccelerometerEnabled_ ) + [[UIAccelerometer sharedAccelerometer] setDelegate:nil]; + +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + if( isMouseEnabled_ ) + [[CCEventDispatcher sharedDispatcher] removeMouseDelegate:self]; + + if( isKeyboardEnabled_ ) + [[CCEventDispatcher sharedDispatcher] removeKeyboardDelegate:self]; + + if( isTouchEnabled_ ) + [[CCEventDispatcher sharedDispatcher] removeTouchDelegate:self]; + +#endif + + [super onExit]; +} + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event +{ + NSAssert(NO, @"Layer#ccTouchBegan override me"); + return YES; +} +#endif +@end + +#pragma mark - +#pragma mark LayerColor + +@interface CCLayerColor (Private) +-(void) updateColor; +@end + +@implementation CCLayerColor + +// Opacity and RGB color protocol +@synthesize opacity = opacity_, color = color_; +@synthesize blendFunc = blendFunc_; + + ++ (id) layerWithColor:(ccColor4B)color width:(GLfloat)w height:(GLfloat) h +{ + return [[[self alloc] initWithColor:color width:w height:h] autorelease]; +} + ++ (id) layerWithColor:(ccColor4B)color +{ + return [[(CCLayerColor*)[self alloc] initWithColor:color] autorelease]; +} + +- (id) initWithColor:(ccColor4B)color width:(GLfloat)w height:(GLfloat) h +{ + if( (self=[super init]) ) { + + // default blend function + blendFunc_ = (ccBlendFunc) { CC_BLEND_SRC, CC_BLEND_DST }; + + color_.r = color.r; + color_.g = color.g; + color_.b = color.b; + opacity_ = color.a; + + for (NSUInteger i = 0; i +{ + tCCMenuState state_; + CCMenuItem *selectedItem_; + GLubyte opacity_; + ccColor3B color_; +} + +/** creates a CCMenu with it's items */ ++ (id) menuWithItems: (CCMenuItem*) item, ... NS_REQUIRES_NIL_TERMINATION; + +/** initializes a CCMenu with it's items */ +- (id) initWithItems: (CCMenuItem*) item vaList: (va_list) args; + +/** align items vertically */ +-(void) alignItemsVertically; +/** align items vertically with padding + @since v0.7.2 + */ +-(void) alignItemsVerticallyWithPadding:(float) padding; + +/** align items horizontally */ +-(void) alignItemsHorizontally; +/** align items horizontally with padding + @since v0.7.2 + */ +-(void) alignItemsHorizontallyWithPadding: (float) padding; + + +/** align items in rows of columns */ +-(void) alignItemsInColumns: (NSNumber *) columns, ... NS_REQUIRES_NIL_TERMINATION; +-(void) alignItemsInColumns: (NSNumber *) columns vaList: (va_list) args; + +/** align items in columns of rows */ +-(void) alignItemsInRows: (NSNumber *) rows, ... NS_REQUIRES_NIL_TERMINATION; +-(void) alignItemsInRows: (NSNumber *) rows vaList: (va_list) args; + + +/** conforms to CCRGBAProtocol protocol */ +@property (nonatomic,readonly) GLubyte opacity; +/** conforms to CCRGBAProtocol protocol */ +@property (nonatomic,readonly) ccColor3B color; + +@end diff --git a/libs/cocos2d/CCMenu.m b/libs/cocos2d/CCMenu.m new file mode 100644 index 0000000..4156672 --- /dev/null +++ b/libs/cocos2d/CCMenu.m @@ -0,0 +1,522 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CCMenu.h" +#import "CCDirector.h" +#import "Support/CGPointExtension.h" +#import "ccMacros.h" + +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "Platforms/iOS/CCDirectorIOS.h" +#import "Platforms/iOS/CCTouchDispatcher.h" +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import "Platforms/Mac/MacGLView.h" +#import "Platforms/Mac/CCDirectorMac.h" +#endif + +enum { + kDefaultPadding = 5, +}; + +@implementation CCMenu + +@synthesize opacity = opacity_, color = color_; + +- (id) init +{ + NSAssert(NO, @"CCMenu: Init not supported."); + [self release]; + return nil; +} + ++(id) menuWithItems: (CCMenuItem*) item, ... +{ + va_list args; + va_start(args,item); + + id s = [[[self alloc] initWithItems: item vaList:args] autorelease]; + + va_end(args); + return s; +} + +-(id) initWithItems: (CCMenuItem*) item vaList: (va_list) args +{ + if( (self=[super init]) ) { + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + self.isTouchEnabled = YES; +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + self.isMouseEnabled = YES; +#endif + + // menu in the center of the screen + CGSize s = [[CCDirector sharedDirector] winSize]; + + self.isRelativeAnchorPoint = NO; + anchorPoint_ = ccp(0.5f, 0.5f); + [self setContentSize:s]; + + // XXX: in v0.7, winSize should return the visible size + // XXX: so the bar calculation should be done there +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + CGRect r = [[UIApplication sharedApplication] statusBarFrame]; + ccDeviceOrientation orientation = [[CCDirector sharedDirector] deviceOrientation]; + if( orientation == CCDeviceOrientationLandscapeLeft || orientation == CCDeviceOrientationLandscapeRight ) + s.height -= r.size.width; + else + s.height -= r.size.height; +#endif + self.position = ccp(s.width/2, s.height/2); + + int z=0; + + if (item) { + [self addChild: item z:z]; + CCMenuItem *i = va_arg(args, CCMenuItem*); + while(i) { + z++; + [self addChild: i z:z]; + i = va_arg(args, CCMenuItem*); + } + } + // [self alignItemsVertically]; + + selectedItem_ = nil; + state_ = kCCMenuStateWaiting; + } + + return self; +} + +-(void) dealloc +{ + [super dealloc]; +} + +/* + * override add: + */ +-(void) addChild:(CCMenuItem*)child z:(NSInteger)z tag:(NSInteger) aTag +{ + NSAssert( [child isKindOfClass:[CCMenuItem class]], @"Menu only supports MenuItem objects as children"); + [super addChild:child z:z tag:aTag]; +} + +- (void) onExit +{ + if(state_ == kCCMenuStateTrackingTouch) + { + [selectedItem_ unselected]; + state_ = kCCMenuStateWaiting; + selectedItem_ = nil; + } + [super onExit]; +} + +#pragma mark Menu - Touches + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +-(void) registerWithTouchDispatcher +{ + [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:kCCMenuTouchPriority swallowsTouches:YES]; +} + +-(CCMenuItem *) itemForTouch: (UITouch *) touch +{ + CGPoint touchLocation = [touch locationInView: [touch view]]; + touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation]; + + CCMenuItem* item; + CCARRAY_FOREACH(children_, item){ + // ignore invisible and disabled items: issue #779, #866 + if ( [item visible] && [item isEnabled] ) { + + CGPoint local = [item convertToNodeSpace:touchLocation]; + CGRect r = [item rect]; + r.origin = CGPointZero; + + if( CGRectContainsPoint( r, local ) ) + return item; + } + } + return nil; +} + +-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event +{ + if( state_ != kCCMenuStateWaiting || !visible_ ) + return NO; + + selectedItem_ = [self itemForTouch:touch]; + [selectedItem_ selected]; + + if( selectedItem_ ) { + state_ = kCCMenuStateTrackingTouch; + return YES; + } + return NO; +} + +-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event +{ + NSAssert(state_ == kCCMenuStateTrackingTouch, @"[Menu ccTouchEnded] -- invalid state"); + + [selectedItem_ unselected]; + [selectedItem_ activate]; + + state_ = kCCMenuStateWaiting; +} + +-(void) ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event +{ + NSAssert(state_ == kCCMenuStateTrackingTouch, @"[Menu ccTouchCancelled] -- invalid state"); + + [selectedItem_ unselected]; + + state_ = kCCMenuStateWaiting; +} + +-(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event +{ + NSAssert(state_ == kCCMenuStateTrackingTouch, @"[Menu ccTouchMoved] -- invalid state"); + + CCMenuItem *currentItem = [self itemForTouch:touch]; + + if (currentItem != selectedItem_) { + [selectedItem_ unselected]; + selectedItem_ = currentItem; + [selectedItem_ selected]; + } +} + +#pragma mark Menu - Mouse + +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +-(NSInteger) mouseDelegatePriority +{ + return kCCMenuMousePriority+1; +} + +-(CCMenuItem *) itemForMouseEvent: (NSEvent *) event +{ + CGPoint location = [(CCDirectorMac*)[CCDirector sharedDirector] convertEventToGL:event]; + + CCMenuItem* item; + CCARRAY_FOREACH(children_, item){ + // ignore invisible and disabled items: issue #779, #866 + if ( [item visible] && [item isEnabled] ) { + + CGPoint local = [item convertToNodeSpace:location]; + + CGRect r = [item rect]; + r.origin = CGPointZero; + + if( CGRectContainsPoint( r, local ) ) + return item; + } + } + return nil; +} + +-(BOOL) ccMouseUp:(NSEvent *)event +{ + if( ! visible_ ) + return NO; + + if(state_ == kCCMenuStateTrackingTouch) { + if( selectedItem_ ) { + [selectedItem_ unselected]; + [selectedItem_ activate]; + } + state_ = kCCMenuStateWaiting; + + return YES; + } + return NO; +} + +-(BOOL) ccMouseDown:(NSEvent *)event +{ + if( ! visible_ ) + return NO; + + selectedItem_ = [self itemForMouseEvent:event]; + [selectedItem_ selected]; + + if( selectedItem_ ) { + state_ = kCCMenuStateTrackingTouch; + return YES; + } + + return NO; +} + +-(BOOL) ccMouseDragged:(NSEvent *)event +{ + if( ! visible_ ) + return NO; + + if(state_ == kCCMenuStateTrackingTouch) { + CCMenuItem *currentItem = [self itemForMouseEvent:event]; + + if (currentItem != selectedItem_) { + [selectedItem_ unselected]; + selectedItem_ = currentItem; + [selectedItem_ selected]; + } + + return YES; + } + return NO; +} + +#endif // Mac Mouse support + +#pragma mark Menu - Alignment +-(void) alignItemsVertically +{ + return [self alignItemsVerticallyWithPadding:kDefaultPadding]; +} +-(void) alignItemsVerticallyWithPadding:(float)padding +{ + float height = -padding; + + CCMenuItem *item; + CCARRAY_FOREACH(children_, item) + height += item.contentSize.height * item.scaleY + padding; + + float y = height / 2.0f; + + CCARRAY_FOREACH(children_, item) { + CGSize itemSize = item.contentSize; + [item setPosition:ccp(0, y - itemSize.height * item.scaleY / 2.0f)]; + y -= itemSize.height * item.scaleY + padding; + } +} + +-(void) alignItemsHorizontally +{ + return [self alignItemsHorizontallyWithPadding:kDefaultPadding]; +} + +-(void) alignItemsHorizontallyWithPadding:(float)padding +{ + + float width = -padding; + CCMenuItem *item; + CCARRAY_FOREACH(children_, item) + width += item.contentSize.width * item.scaleX + padding; + + float x = -width / 2.0f; + + CCARRAY_FOREACH(children_, item){ + CGSize itemSize = item.contentSize; + [item setPosition:ccp(x + itemSize.width * item.scaleX / 2.0f, 0)]; + x += itemSize.width * item.scaleX + padding; + } +} + +-(void) alignItemsInColumns: (NSNumber *) columns, ... +{ + va_list args; + va_start(args, columns); + + [self alignItemsInColumns:columns vaList:args]; + + va_end(args); +} + +-(void) alignItemsInColumns: (NSNumber *) columns vaList: (va_list) args +{ + NSMutableArray *rows = [[NSMutableArray alloc] initWithObjects:columns, nil]; + columns = va_arg(args, NSNumber*); + while(columns) { + [rows addObject:columns]; + columns = va_arg(args, NSNumber*); + } + + int height = -5; + NSUInteger row = 0, rowHeight = 0, columnsOccupied = 0, rowColumns; + CCMenuItem *item; + CCARRAY_FOREACH(children_, item){ + NSAssert( row < [rows count], @"Too many menu items for the amount of rows/columns."); + + rowColumns = [(NSNumber *) [rows objectAtIndex:row] unsignedIntegerValue]; + NSAssert( rowColumns, @"Can't have zero columns on a row"); + + rowHeight = fmaxf(rowHeight, item.contentSize.height); + ++columnsOccupied; + + if(columnsOccupied >= rowColumns) { + height += rowHeight + 5; + + columnsOccupied = 0; + rowHeight = 0; + ++row; + } + } + NSAssert( !columnsOccupied, @"Too many rows/columns for available menu items." ); + + CGSize winSize = [[CCDirector sharedDirector] winSize]; + + row = 0; rowHeight = 0; rowColumns = 0; + float w, x, y = height / 2; + CCARRAY_FOREACH(children_, item) { + if(rowColumns == 0) { + rowColumns = [(NSNumber *) [rows objectAtIndex:row] unsignedIntegerValue]; + w = winSize.width / (1 + rowColumns); + x = w; + } + + CGSize itemSize = item.contentSize; + rowHeight = fmaxf(rowHeight, itemSize.height); + [item setPosition:ccp(x - winSize.width / 2, + y - itemSize.height / 2)]; + + x += w + 10; + ++columnsOccupied; + + if(columnsOccupied >= rowColumns) { + y -= rowHeight + 5; + + columnsOccupied = 0; + rowColumns = 0; + rowHeight = 0; + ++row; + } + } + + [rows release]; +} + +-(void) alignItemsInRows: (NSNumber *) rows, ... +{ + va_list args; + va_start(args, rows); + + [self alignItemsInRows:rows vaList:args]; + + va_end(args); +} + +-(void) alignItemsInRows: (NSNumber *) rows vaList: (va_list) args +{ + NSMutableArray *columns = [[NSMutableArray alloc] initWithObjects:rows, nil]; + rows = va_arg(args, NSNumber*); + while(rows) { + [columns addObject:rows]; + rows = va_arg(args, NSNumber*); + } + + NSMutableArray *columnWidths = [[NSMutableArray alloc] init]; + NSMutableArray *columnHeights = [[NSMutableArray alloc] init]; + + int width = -10, columnHeight = -5; + NSUInteger column = 0, columnWidth = 0, rowsOccupied = 0, columnRows; + CCMenuItem *item; + CCARRAY_FOREACH(children_, item){ + NSAssert( column < [columns count], @"Too many menu items for the amount of rows/columns."); + + columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue]; + NSAssert( columnRows, @"Can't have zero rows on a column"); + + CGSize itemSize = item.contentSize; + columnWidth = fmaxf(columnWidth, itemSize.width); + columnHeight += itemSize.height + 5; + ++rowsOccupied; + + if(rowsOccupied >= columnRows) { + [columnWidths addObject:[NSNumber numberWithUnsignedInteger:columnWidth]]; + [columnHeights addObject:[NSNumber numberWithUnsignedInteger:columnHeight]]; + width += columnWidth + 10; + + rowsOccupied = 0; + columnWidth = 0; + columnHeight = -5; + ++column; + } + } + NSAssert( !rowsOccupied, @"Too many rows/columns for available menu items."); + + CGSize winSize = [[CCDirector sharedDirector] winSize]; + + column = 0; columnWidth = 0; columnRows = 0; + float x = -width / 2, y; + + CCARRAY_FOREACH(children_, item){ + if(columnRows == 0) { + columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue]; + y = ([(NSNumber *) [columnHeights objectAtIndex:column] intValue] + winSize.height) / 2; + } + + CGSize itemSize = item.contentSize; + columnWidth = fmaxf(columnWidth, itemSize.width); + [item setPosition:ccp(x + [(NSNumber *) [columnWidths objectAtIndex:column] unsignedIntegerValue] / 2, + y - winSize.height / 2)]; + + y -= itemSize.height + 10; + ++rowsOccupied; + + if(rowsOccupied >= columnRows) { + x += columnWidth + 5; + + rowsOccupied = 0; + columnRows = 0; + columnWidth = 0; + ++column; + } + } + + [columns release]; + [columnWidths release]; + [columnHeights release]; +} + +#pragma mark Menu - Opacity Protocol + +/** Override synthesized setOpacity to recurse items */ +- (void) setOpacity:(GLubyte)newOpacity +{ + opacity_ = newOpacity; + + id item; + CCARRAY_FOREACH(children_, item) + [item setOpacity:opacity_]; +} + +-(void) setColor:(ccColor3B)color +{ + color_ = color; + + id item; + CCARRAY_FOREACH(children_, item) + [item setColor:color_]; +} +@end diff --git a/libs/cocos2d/CCMenuItem.h b/libs/cocos2d/CCMenuItem.h new file mode 100644 index 0000000..3da8c6b --- /dev/null +++ b/libs/cocos2d/CCMenuItem.h @@ -0,0 +1,362 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2011 Ricardo Quesada + * + * 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 "CCBlockSupport.h" + +#import "CCNode.h" +#import "CCProtocols.h" + +@class CCSprite; + +#define kItemSize 32 + +#pragma mark - +#pragma mark CCMenuItem +/** CCMenuItem base class + * + * Subclass CCMenuItem (or any subclass) to create your custom CCMenuItem objects. + */ +@interface CCMenuItem : CCNode +{ + NSInvocation *invocation; +#if NS_BLOCKS_AVAILABLE + // used for menu items using a block + void (^block_)(id sender); +#endif + + BOOL isEnabled_; + BOOL isSelected_; +} + +/** returns whether or not the item is selected +@since v0.8.2 +*/ +@property (nonatomic,readonly) BOOL isSelected; + +/** Creates a CCMenuItem with a target/selector */ ++(id) itemWithTarget:(id)target selector:(SEL)selector; + +/** Initializes a CCMenuItem with a target/selector */ +-(id) initWithTarget:(id)target selector:(SEL)selector; + +#if NS_BLOCKS_AVAILABLE +/** Creates a CCMenuItem with the specified block. + The block will be "copied". + */ ++(id) itemWithBlock:(void(^)(id sender))block; + +/** Initializes a CCMenuItem with the specified block. + The block will be "copied". +*/ +-(id) initWithBlock:(void(^)(id sender))block; +#endif + +/** Returns the outside box in points */ +-(CGRect) rect; + +/** Activate the item */ +-(void) activate; + +/** The item was selected (not activated), similar to "mouse-over" */ +-(void) selected; + +/** The item was unselected */ +-(void) unselected; + +/** Enable or disabled the CCMenuItem */ +-(void) setIsEnabled:(BOOL)enabled; +/** Returns whether or not the CCMenuItem is enabled */ +-(BOOL) isEnabled; +@end + +#pragma mark - +#pragma mark CCMenuItemLabel + +/** An abstract class for "label" CCMenuItemLabel items + Any CCNode that supports the CCLabelProtocol protocol can be added. + Supported nodes: + - CCLabelBMFont + - CCLabelAtlas + - CCLabelTTF + */ +@interface CCMenuItemLabel : CCMenuItem +{ + CCNode *label_; + ccColor3B colorBackup; + ccColor3B disabledColor_; + float originalScale_; +} + +/** the color that will be used to disable the item */ +@property (nonatomic,readwrite) ccColor3B disabledColor; + +/** Label that is rendered. It can be any CCNode that implements the CCLabelProtocol */ +@property (nonatomic,readwrite,assign) CCNode* label; + +/** creates a CCMenuItemLabel with a Label. Target and selector will be nill */ ++(id) itemWithLabel:(CCNode*)label; + +/** creates a CCMenuItemLabel with a Label, target and selector */ ++(id) itemWithLabel:(CCNode*)label target:(id)target selector:(SEL)selector; + +/** initializes a CCMenuItemLabel with a Label, target and selector */ +-(id) initWithLabel:(CCNode*)label target:(id)target selector:(SEL)selector; + +#if NS_BLOCKS_AVAILABLE +/** creates a CCMenuItemLabel with a Label and a block to execute. + The block will be "copied". + */ ++(id) itemWithLabel:(CCNode*)label block:(void(^)(id sender))block; + +/** initializes a CCMenuItemLabel with a Label and a block to execute. + The block will be "copied". + */ +-(id) initWithLabel:(CCNode*)label block:(void(^)(id sender))block; +#endif + +/** sets a new string to the inner label */ +-(void) setString:(NSString*)label; + +/** Enable or disabled the CCMenuItemFont + @warning setIsEnabled changes the RGB color of the font + */ +-(void) setIsEnabled: (BOOL)enabled; +@end + +#pragma mark - +#pragma mark CCMenuItemAtlasFont + +/** A CCMenuItemAtlasFont + Helper class that creates a MenuItemLabel class with a LabelAtlas + */ +@interface CCMenuItemAtlasFont : CCMenuItemLabel +{ +} + +/** creates a menu item from a string and atlas with a target/selector */ ++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap; + +/** creates a menu item from a string and atlas. Use it with MenuItemToggle */ ++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb; + +/** initializes a menu item from a string and atlas with a target/selector */ +-(id) initFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb; + +#if NS_BLOCKS_AVAILABLE +/** creates a menu item from a string and atlas. Use it with MenuItemToggle. + The block will be "copied". + */ ++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block; + +/** initializes a menu item from a string and atlas with a block. + The block will be "copied". + */ +-(id) initFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block; +#endif + +@end + +#pragma mark - +#pragma mark CCMenuItemFont + +/** A CCMenuItemFont + Helper class that creates a CCMenuItemLabel class with a Label + */ +@interface CCMenuItemFont : CCMenuItemLabel +{ +} +/** set font size */ ++(void) setFontSize: (int) s; + +/** get font size */ ++(int) fontSize; + +/** set the font name */ ++(void) setFontName: (NSString*) n; + +/** get the font name */ ++(NSString*) fontName; + +/** creates a menu item from a string without target/selector. To be used with CCMenuItemToggle */ ++(id) itemFromString: (NSString*) value; + +/** creates a menu item from a string with a target/selector */ ++(id) itemFromString: (NSString*) value target:(id) r selector:(SEL) s; + +/** initializes a menu item from a string with a target/selector */ +-(id) initFromString: (NSString*) value target:(id) r selector:(SEL) s; + +#if NS_BLOCKS_AVAILABLE +/** creates a menu item from a string with the specified block. + The block will be "copied". + */ ++(id) itemFromString: (NSString*) value block:(void(^)(id sender))block; + +/** initializes a menu item from a string with the specified block. + The block will be "copied". + */ +-(id) initFromString: (NSString*) value block:(void(^)(id sender))block; +#endif +@end + +#pragma mark - +#pragma mark CCMenuItemSprite + +/** CCMenuItemSprite accepts CCNode objects as items. + The images has 3 different states: + - unselected image + - selected image + - disabled image + + @since v0.8.0 + */ +@interface CCMenuItemSprite : CCMenuItem +{ + CCNode *normalImage_, *selectedImage_, *disabledImage_; +} + +// weak references + +/** the image used when the item is not selected */ +@property (nonatomic,readwrite,assign) CCNode *normalImage; +/** the image used when the item is selected */ +@property (nonatomic,readwrite,assign) CCNode *selectedImage; +/** the image used when the item is disabled */ +@property (nonatomic,readwrite,assign) CCNode *disabledImage; + +/** creates a menu item with a normal and selected image*/ ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite; +/** creates a menu item with a normal and selected image with target/selector */ ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite target:(id)target selector:(SEL)selector; +/** creates a menu item with a normal,selected and disabled image with target/selector */ ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite target:(id)target selector:(SEL)selector; +/** initializes a menu item with a normal, selected and disabled image with target/selector */ +-(id) initFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite target:(id)target selector:(SEL)selector; + +#if NS_BLOCKS_AVAILABLE +/** creates a menu item with a normal and selected image with a block. + The block will be "copied". + */ ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite block:(void(^)(id sender))block; +/** creates a menu item with a normal,selected and disabled image with a block. + The block will be "copied". + */ ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite block:(void(^)(id sender))block; +/** initializes a menu item with a normal, selected and disabled image with a block. + The block will be "copied". + */ +-(id) initFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite block:(void(^)(id sender))block; +#endif + +@end + +#pragma mark - +#pragma mark CCMenuItemImage + +/** CCMenuItemImage accepts images as items. + The images has 3 different states: + - unselected image + - selected image + - disabled image + + For best results try that all images are of the same size + */ +@interface CCMenuItemImage : CCMenuItemSprite +{ +} + +/** creates a menu item with a normal and selected image*/ ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2; +/** creates a menu item with a normal and selected image with target/selector */ ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 target:(id) r selector:(SEL) s; +/** creates a menu item with a normal,selected and disabled image with target/selector */ ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 target:(id) r selector:(SEL) s; +/** initializes a menu item with a normal, selected and disabled image with target/selector */ +-(id) initFromNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 target:(id) r selector:(SEL) s; +#if NS_BLOCKS_AVAILABLE +/** creates a menu item with a normal and selected image with a block. + The block will be "copied". + */ ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 block:(void(^)(id sender))block; +/** creates a menu item with a normal,selected and disabled image with a block. + The block will be "copied". +*/ ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block; +/** initializes a menu item with a normal, selected and disabled image with a block. + The block will be "copied". +*/ +-(id) initFromNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block; +#endif +@end + +#pragma mark - +#pragma mark CCMenuItemToggle + +/** A CCMenuItemToggle + A simple container class that "toggles" it's inner items + The inner itmes can be any MenuItem + */ +@interface CCMenuItemToggle : CCMenuItem +{ + NSUInteger selectedIndex_; + NSMutableArray* subItems_; + GLubyte opacity_; + ccColor3B color_; +} + +/** conforms with CCRGBAProtocol protocol */ +@property (nonatomic,readonly) GLubyte opacity; +/** conforms with CCRGBAProtocol protocol */ +@property (nonatomic,readonly) ccColor3B color; + +/** returns the selected item */ +@property (nonatomic,readwrite) NSUInteger selectedIndex; +/** NSMutableArray that contains the subitems. You can add/remove items in runtime, and you can replace the array with a new one. + @since v0.7.2 + */ +@property (nonatomic,readwrite,retain) NSMutableArray *subItems; + +/** creates a menu item from a list of items with a target/selector */ ++(id) itemWithTarget:(id)t selector:(SEL)s items:(CCMenuItem*) item, ... NS_REQUIRES_NIL_TERMINATION; + +/** initializes a menu item from a list of items with a target selector */ +-(id) initWithTarget:(id)t selector:(SEL)s items:(CCMenuItem*) item vaList:(va_list) args; + +#if NS_BLOCKS_AVAILABLE +/** creates a menu item from a list of items and executes the given block when the item is selected. + The block will be "copied". + */ ++(id) itemWithBlock:(void(^)(id sender))block items:(CCMenuItem*)item, ... NS_REQUIRES_NIL_TERMINATION; + +/** initializes a menu item from a list of items with a block. + The block will be "copied". + */ +-(id) initWithBlock:(void (^)(id))block items:(CCMenuItem*)item vaList:(va_list)args; +#endif + +/** return the selected item */ +-(CCMenuItem*) selectedItem; +@end + diff --git a/libs/cocos2d/CCMenuItem.m b/libs/cocos2d/CCMenuItem.m new file mode 100644 index 0000000..5e46b9f --- /dev/null +++ b/libs/cocos2d/CCMenuItem.m @@ -0,0 +1,762 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2011 Ricardo Quesada + * + * 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 "CCMenuItem.h" +#import "CCLabelTTF.h" +#import "CCLabelAtlas.h" +#import "CCActionInterval.h" +#import "CCSprite.h" +#import "Support/CGPointExtension.h" + +static int _fontSize = kItemSize; +static NSString *_fontName = @"Marker Felt"; +static BOOL _fontNameRelease = NO; + +enum { + kCurrentItem = 0xc0c05001, +}; + +enum { + kZoomActionTag = 0xc0c05002, +}; + + + +#pragma mark - +#pragma mark CCMenuItem + +@implementation CCMenuItem + +@synthesize isSelected=isSelected_; +-(id) init +{ + NSAssert(NO, @"MenuItemInit: Init not supported."); + [self release]; + return nil; +} + ++(id) itemWithTarget:(id) r selector:(SEL) s +{ + return [[[self alloc] initWithTarget:r selector:s] autorelease]; +} + +-(id) initWithTarget:(id) rec selector:(SEL) cb +{ + if((self=[super init]) ) { + + anchorPoint_ = ccp(0.5f, 0.5f); + NSMethodSignature * sig = nil; + + if( rec && cb ) { + sig = [rec methodSignatureForSelector:cb]; + + invocation = nil; + invocation = [NSInvocation invocationWithMethodSignature:sig]; + [invocation setTarget:rec]; + [invocation setSelector:cb]; +#if NS_BLOCKS_AVAILABLE + if ([sig numberOfArguments] == 3) +#endif + [invocation setArgument:&self atIndex:2]; + + [invocation retain]; + } + + isEnabled_ = YES; + isSelected_ = NO; + } + + return self; +} + +#if NS_BLOCKS_AVAILABLE + ++(id) itemWithBlock:(void(^)(id sender))block { + return [[[self alloc] initWithBlock:block] autorelease]; +} + +-(id) initWithBlock:(void(^)(id sender))block { + block_ = [block copy]; + return [self initWithTarget:block_ selector:@selector(ccCallbackBlockWithSender:)]; +} + +#endif // NS_BLOCKS_AVAILABLE + +-(void) dealloc +{ + [invocation release]; + +#if NS_BLOCKS_AVAILABLE + [block_ release]; +#endif + + [super dealloc]; +} + +-(void) selected +{ + isSelected_ = YES; +} + +-(void) unselected +{ + isSelected_ = NO; +} + +-(void) activate +{ + if(isEnabled_) + [invocation invoke]; +} + +-(void) setIsEnabled: (BOOL)enabled +{ + isEnabled_ = enabled; +} + +-(BOOL) isEnabled +{ + return isEnabled_; +} + +-(CGRect) rect +{ + return CGRectMake( position_.x - contentSize_.width*anchorPoint_.x, + position_.y - contentSize_.height*anchorPoint_.y, + contentSize_.width, contentSize_.height); +} + +@end + + +#pragma mark - +#pragma mark CCMenuItemLabel + +@implementation CCMenuItemLabel + +@synthesize disabledColor = disabledColor_; + ++(id) itemWithLabel:(CCNode*)label target:(id)target selector:(SEL)selector +{ + return [[[self alloc] initWithLabel:label target:target selector:selector] autorelease]; +} + ++(id) itemWithLabel:(CCNode*)label +{ + return [[[self alloc] initWithLabel:label target:nil selector:NULL] autorelease]; +} + +-(id) initWithLabel:(CCNode*)label target:(id)target selector:(SEL)selector +{ + if( (self=[super initWithTarget:target selector:selector]) ) { + originalScale_ = 1; + colorBackup = ccWHITE; + disabledColor_ = ccc3( 126,126,126); + self.label = label; + + } + return self; +} + +#if NS_BLOCKS_AVAILABLE + ++(id) itemWithLabel:(CCNode*)label block:(void(^)(id sender))block { + return [[[self alloc] initWithLabel:label block:block] autorelease]; +} + +-(id) initWithLabel:(CCNode*)label block:(void(^)(id sender))block { + block_ = [block copy]; + return [self initWithLabel:label target:block_ selector:@selector(ccCallbackBlockWithSender:)]; +} + +#endif // NS_BLOCKS_AVAILABLE + +-(CCNode*) label +{ + return label_; +} +-(void) setLabel:(CCNode*) label +{ + if( label != label_ ) { + [self removeChild:label_ cleanup:YES]; + [self addChild:label]; + + label_ = label; + label_.anchorPoint = ccp(0,0); + + [self setContentSize:[label_ contentSize]]; + } +} + +-(void) setString:(NSString *)string +{ + [label_ setString:string]; + [self setContentSize: [label_ contentSize]]; +} + +-(void) activate { + if(isEnabled_) { + [self stopAllActions]; + + self.scale = originalScale_; + + [super activate]; + } +} + +-(void) selected +{ + // subclass to change the default action + if(isEnabled_) { + [super selected]; + + CCAction *action = [self getActionByTag:kZoomActionTag]; + if( action ) + [self stopAction:action]; + else + originalScale_ = self.scale; + + CCAction *zoomAction = [CCScaleTo actionWithDuration:0.1f scale:originalScale_ * 1.2f]; + zoomAction.tag = kZoomActionTag; + [self runAction:zoomAction]; + } +} + +-(void) unselected +{ + // subclass to change the default action + if(isEnabled_) { + [super unselected]; + [self stopActionByTag:kZoomActionTag]; + CCAction *zoomAction = [CCScaleTo actionWithDuration:0.1f scale:originalScale_]; + zoomAction.tag = kZoomActionTag; + [self runAction:zoomAction]; + } +} + +-(void) setIsEnabled: (BOOL)enabled +{ + if( isEnabled_ != enabled ) { + if(enabled == NO) { + colorBackup = [label_ color]; + [label_ setColor: disabledColor_]; + } + else + [label_ setColor:colorBackup]; + } + + [super setIsEnabled:enabled]; +} + +- (void) setOpacity: (GLubyte)opacity +{ + [label_ setOpacity:opacity]; +} +-(GLubyte) opacity +{ + return [label_ opacity]; +} +-(void) setColor:(ccColor3B)color +{ + [label_ setColor:color]; +} +-(ccColor3B) color +{ + return [label_ color]; +} +@end + +#pragma mark - +#pragma mark CCMenuItemAtlasFont + +@implementation CCMenuItemAtlasFont + ++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap +{ + return [CCMenuItemAtlasFont itemFromString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap target:nil selector:nil]; +} + ++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb +{ + return [[[self alloc] initFromString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap target:rec selector:cb] autorelease]; +} + +-(id) initFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb +{ + NSAssert( [value length] != 0, @"value length must be greater than 0"); + + CCLabelAtlas *label = [[CCLabelAtlas alloc] initWithString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap]; + [label autorelease]; + + if((self=[super initWithLabel:label target:rec selector:cb]) ) { + // do something ? + } + + return self; +} + +#if NS_BLOCKS_AVAILABLE ++(id) itemFromString:(NSString*)value charMapFile:(NSString*)charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block { + return [[[self alloc] initFromString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap block:block] autorelease]; +} + +-(id) initFromString:(NSString*)value charMapFile:(NSString*)charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block { + block_ = [block copy]; + return [self initFromString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap target:block_ selector:@selector(ccCallbackBlockWithSender:)]; +} +#endif // NS_BLOCKS_AVAILABLE + +-(void) dealloc +{ + [super dealloc]; +} +@end + + +#pragma mark - +#pragma mark CCMenuItemFont + +@implementation CCMenuItemFont + ++(void) setFontSize: (int) s +{ + _fontSize = s; +} + ++(int) fontSize +{ + return _fontSize; +} + ++(void) setFontName: (NSString*) n +{ + if( _fontNameRelease ) + [_fontName release]; + + _fontName = [n retain]; + _fontNameRelease = YES; +} + ++(NSString*) fontName +{ + return _fontName; +} + ++(id) itemFromString: (NSString*) value target:(id) r selector:(SEL) s +{ + return [[[self alloc] initFromString: value target:r selector:s] autorelease]; +} + ++(id) itemFromString: (NSString*) value +{ + return [[[self alloc] initFromString: value target:nil selector:nil] autorelease]; +} + +-(id) initFromString: (NSString*) value target:(id) rec selector:(SEL) cb +{ + NSAssert( [value length] != 0, @"Value length must be greater than 0"); + + CCLabelTTF *label = [CCLabelTTF labelWithString:value fontName:_fontName fontSize:_fontSize]; + + if((self=[super initWithLabel:label target:rec selector:cb]) ) { + // do something ? + } + + return self; +} + +#if NS_BLOCKS_AVAILABLE ++(id) itemFromString: (NSString*) value block:(void(^)(id sender))block { + return [[[self alloc] initFromString:value block:block] autorelease]; +} + +-(id) initFromString: (NSString*) value block:(void(^)(id sender))block { + block_ = [block copy]; + return [self initFromString:value target:block_ selector:@selector(ccCallbackBlockWithSender:)]; +} +#endif // NS_BLOCKS_AVAILABLE + +@end + +#pragma mark - +#pragma mark CCMenuItemSprite +@implementation CCMenuItemSprite + +@synthesize normalImage=normalImage_, selectedImage=selectedImage_, disabledImage=disabledImage_; + ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite +{ + return [self itemFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil target:nil selector:nil]; +} ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite target:(id)target selector:(SEL)selector +{ + return [self itemFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil target:target selector:selector]; +} ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite target:(id)target selector:(SEL)selector +{ + return [[[self alloc] initFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite target:target selector:selector] autorelease]; +} +-(id) initFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite target:(id)target selector:(SEL)selector +{ + if( (self=[super initWithTarget:target selector:selector]) ) { + + self.normalImage = normalSprite; + self.selectedImage = selectedSprite; + self.disabledImage = disabledSprite; + + [self setContentSize: [normalImage_ contentSize]]; + } + return self; +} + +#if NS_BLOCKS_AVAILABLE ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite block:(void(^)(id sender))block { + return [self itemFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil block:block]; +} + ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite block:(void(^)(id sender))block { + return [[[self alloc] initFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite block:block] autorelease]; +} + +-(id) initFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite block:(void(^)(id sender))block { + block_ = [block copy]; + return [self initFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite target:block_ selector:@selector(ccCallbackBlockWithSender:)]; +} +#endif // NS_BLOCKS_AVAILABLE + + +-(void) setNormalImage:(CCNode *)image +{ + if( image != normalImage_ ) { + image.anchorPoint = ccp(0,0); + image.visible = YES; + + [self removeChild:normalImage_ cleanup:YES]; + [self addChild:image]; + + normalImage_ = image; + } +} + +-(void) setSelectedImage:(CCNode *)image +{ + if( image != selectedImage_ ) { + image.anchorPoint = ccp(0,0); + image.visible = NO; + + [self removeChild:selectedImage_ cleanup:YES]; + [self addChild:image]; + + selectedImage_ = image; + } +} + +-(void) setDisabledImage:(CCNode *)image +{ + if( image != disabledImage_ ) { + image.anchorPoint = ccp(0,0); + image.visible = NO; + + [self removeChild:disabledImage_ cleanup:YES]; + [self addChild:image]; + + disabledImage_ = image; + } +} + +#pragma mark CCMenuItemImage - CCRGBAProtocol protocol +- (void) setOpacity: (GLubyte)opacity +{ + [normalImage_ setOpacity:opacity]; + [selectedImage_ setOpacity:opacity]; + [disabledImage_ setOpacity:opacity]; +} + +-(void) setColor:(ccColor3B)color +{ + [normalImage_ setColor:color]; + [selectedImage_ setColor:color]; + [disabledImage_ setColor:color]; +} + +-(GLubyte) opacity +{ + return [normalImage_ opacity]; +} + +-(ccColor3B) color +{ + return [normalImage_ color]; +} + +-(void) selected +{ + [super selected]; + + if( selectedImage_ ) { + [normalImage_ setVisible:NO]; + [selectedImage_ setVisible:YES]; + [disabledImage_ setVisible:NO]; + + } else { // there is not selected image + + [normalImage_ setVisible:YES]; + [selectedImage_ setVisible:NO]; + [disabledImage_ setVisible:NO]; + } +} + +-(void) unselected +{ + [super unselected]; + [normalImage_ setVisible:YES]; + [selectedImage_ setVisible:NO]; + [disabledImage_ setVisible:NO]; +} + +-(void) setIsEnabled:(BOOL)enabled +{ + [super setIsEnabled:enabled]; + + if( enabled ) { + [normalImage_ setVisible:YES]; + [selectedImage_ setVisible:NO]; + [disabledImage_ setVisible:NO]; + + } else { + if( disabledImage_ ) { + [normalImage_ setVisible:NO]; + [selectedImage_ setVisible:NO]; + [disabledImage_ setVisible:YES]; + } else { + [normalImage_ setVisible:YES]; + [selectedImage_ setVisible:NO]; + [disabledImage_ setVisible:NO]; + } + } +} + +@end + +#pragma mark - +#pragma mark CCMenuItemImage + +@implementation CCMenuItemImage + ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 +{ + return [self itemFromNormalImage:value selectedImage:value2 disabledImage: nil target:nil selector:nil]; +} + ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 target:(id) t selector:(SEL) s +{ + return [self itemFromNormalImage:value selectedImage:value2 disabledImage: nil target:t selector:s]; +} + ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage: (NSString*) value3 +{ + return [[[self alloc] initFromNormalImage:value selectedImage:value2 disabledImage:value3 target:nil selector:nil] autorelease]; +} + ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage: (NSString*) value3 target:(id) t selector:(SEL) s +{ + return [[[self alloc] initFromNormalImage:value selectedImage:value2 disabledImage:value3 target:t selector:s] autorelease]; +} + +-(id) initFromNormalImage: (NSString*) normalI selectedImage:(NSString*)selectedI disabledImage: (NSString*) disabledI target:(id)t selector:(SEL)sel +{ + CCNode *normalImage = [CCSprite spriteWithFile:normalI]; + CCNode *selectedImage = nil; + CCNode *disabledImage = nil; + + if( selectedI ) + selectedImage = [CCSprite spriteWithFile:selectedI]; + if(disabledI) + disabledImage = [CCSprite spriteWithFile:disabledI]; + + return [self initFromNormalSprite:normalImage selectedSprite:selectedImage disabledSprite:disabledImage target:t selector:sel]; +} + +#if NS_BLOCKS_AVAILABLE + ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 block:(void(^)(id sender))block { + return [self itemFromNormalImage:value selectedImage:value2 disabledImage:nil block:block]; +} + ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block { + return [[[self alloc] initFromNormalImage:value selectedImage:value2 disabledImage:value3 block:block] autorelease]; +} + +-(id) initFromNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block { + block_ = [block copy]; + return [self initFromNormalImage:value selectedImage:value2 disabledImage:value3 target:block_ selector:@selector(ccCallbackBlockWithSender:)]; +} + +#endif // NS_BLOCKS_AVAILABLE + +@end + +#pragma mark - +#pragma mark CCMenuItemToggle + +// +// MenuItemToggle +// +@implementation CCMenuItemToggle + +@synthesize subItems = subItems_; +@synthesize opacity = opacity_, color = color_; + ++(id) itemWithTarget: (id)t selector: (SEL)sel items: (CCMenuItem*) item, ... +{ + va_list args; + va_start(args, item); + + id s = [[[self alloc] initWithTarget: t selector:sel items: item vaList:args] autorelease]; + + va_end(args); + return s; +} + +-(id) initWithTarget: (id)t selector: (SEL)sel items:(CCMenuItem*) item vaList: (va_list) args +{ + if( (self=[super initWithTarget:t selector:sel]) ) { + + self.subItems = [NSMutableArray arrayWithCapacity:2]; + + int z = 0; + CCMenuItem *i = item; + while(i) { + z++; + [subItems_ addObject:i]; + i = va_arg(args, CCMenuItem*); + } + + selectedIndex_ = NSUIntegerMax; + [self setSelectedIndex:0]; + } + + return self; +} + +#if NS_BLOCKS_AVAILABLE + ++(id) itemWithBlock:(void(^)(id sender))block items:(CCMenuItem*)item, ... { + va_list args; + va_start(args, item); + + id s = [[[self alloc] initWithBlock:block items:item vaList:args] autorelease]; + + va_end(args); + return s; +} + +-(id) initWithBlock:(void (^)(id))block items:(CCMenuItem*)item vaList:(va_list)args { + block_ = [block copy]; + return [self initWithTarget:block_ selector:@selector(ccCallbackBlockWithSender:) items:item vaList:args]; +} + +#endif // NS_BLOCKS_AVAILABLE + +-(void) dealloc +{ + [subItems_ release]; + [super dealloc]; +} + +-(void)setSelectedIndex:(NSUInteger)index +{ + if( index != selectedIndex_ ) { + selectedIndex_=index; + [self removeChildByTag:kCurrentItem cleanup:NO]; + + CCMenuItem *item = [subItems_ objectAtIndex:selectedIndex_]; + [self addChild:item z:0 tag:kCurrentItem]; + + CGSize s = [item contentSize]; + [self setContentSize: s]; + item.position = ccp( s.width/2, s.height/2 ); + } +} + +-(NSUInteger) selectedIndex +{ + return selectedIndex_; +} + + +-(void) selected +{ + [super selected]; + [[subItems_ objectAtIndex:selectedIndex_] selected]; +} + +-(void) unselected +{ + [super unselected]; + [[subItems_ objectAtIndex:selectedIndex_] unselected]; +} + +-(void) activate +{ + // update index + if( isEnabled_ ) { + NSUInteger newIndex = (selectedIndex_ + 1) % [subItems_ count]; + [self setSelectedIndex:newIndex]; + + } + + [super activate]; +} + +-(void) setIsEnabled: (BOOL)enabled +{ + [super setIsEnabled:enabled]; + for(CCMenuItem* item in subItems_) + [item setIsEnabled:enabled]; +} + +-(CCMenuItem*) selectedItem +{ + return [subItems_ objectAtIndex:selectedIndex_]; +} + +#pragma mark CCMenuItemToggle - CCRGBAProtocol protocol + +- (void) setOpacity: (GLubyte)opacity +{ + opacity_ = opacity; + for(CCMenuItem* item in subItems_) + [item setOpacity:opacity]; +} + +- (void) setColor:(ccColor3B)color +{ + color_ = color; + for(CCMenuItem* item in subItems_) + [item setColor:color]; +} + +@end diff --git a/libs/cocos2d/CCMotionStreak.h b/libs/cocos2d/CCMotionStreak.h new file mode 100644 index 0000000..e017124 --- /dev/null +++ b/libs/cocos2d/CCMotionStreak.h @@ -0,0 +1,67 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008, 2009 Jason Booth + * + * 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 +#import "CCNode.h" +#import "CCRibbon.h" + +/** + * CCMotionStreak manages a Ribbon based on it's motion in absolute space. + * You construct it with a fadeTime, minimum segment size, texture path, texture + * length and color. The fadeTime controls how long it takes each vertex in + * the streak to fade out, the minimum segment size it how many pixels the + * streak will move before adding a new ribbon segement, and the texture + * length is the how many pixels the texture is stretched across. The texture + * is vertically aligned along the streak segemnts. + * + * Limitations: + * CCMotionStreak, by default, will use the GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA blending function. + * This blending function might not be the correct one for certain textures. + * But you can change it by using: + * [obj setBlendFunc: (ccBlendfunc) {new_src_blend_func, new_dst_blend_func}]; + * + * @since v0.8.1 + */ +@interface CCMotionStreak : CCNode +{ + CCRibbon* ribbon_; + float segThreshold_; + float width_; + CGPoint lastLocation_; +} + +/** Ribbon used by MotionStreak (weak reference) */ +@property (nonatomic,readonly) CCRibbon *ribbon; + +/** creates the a MotionStreak. The image will be loaded using the TextureMgr. */ ++(id)streakWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color; + +/** initializes a MotionStreak. The file will be loaded using the TextureMgr. */ +-(id)initWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color; + +/** polling function */ +-(void)update:(ccTime)delta; + +@end diff --git a/libs/cocos2d/CCMotionStreak.m b/libs/cocos2d/CCMotionStreak.m new file mode 100644 index 0000000..42737b9 --- /dev/null +++ b/libs/cocos2d/CCMotionStreak.m @@ -0,0 +1,104 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008, 2009 Jason Booth + * + * 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. + * + * + ********************************************************* + * + * Motion Streak manages a Ribbon based on it's motion in absolute space. + * You construct it with a fadeTime, minimum segment size, texture path, texture + * length and color. The fadeTime controls how long it takes each vertex in + * the streak to fade out, the minimum segment size it how many pixels the + * streak will move before adding a new ribbon segement, and the texture + * length is the how many pixels the texture is stretched across. The texture + * is vertically aligned along the streak segemnts. + */ + +#import "CCMotionStreak.h" +#import "Support/CGPointExtension.h" + +@implementation CCMotionStreak + +@synthesize ribbon = ribbon_; + ++(id)streakWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color +{ + return [[[self alloc] initWithFade:(float)fade minSeg:seg image:path width:width length:length color:color] autorelease]; +} + +-(id)initWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color +{ + if( (self=[super init])) { + segThreshold_ = seg; + width_ = width; + lastLocation_ = CGPointZero; + ribbon_ = [CCRibbon ribbonWithWidth:width_ image:path length:length color:color fade:fade]; + [self addChild:ribbon_]; + + // update ribbon position. Use schedule:interval and not scheduleUpdated. issue #1075 + [self schedule:@selector(update:) interval:0]; + } + return self; +} + +-(void)update:(ccTime)delta +{ + CGPoint location = [self convertToWorldSpace:CGPointZero]; + [ribbon_ setPosition:ccp(-1*location.x, -1*location.y)]; + float len = ccpLength(ccpSub(lastLocation_, location)); + if (len > segThreshold_) + { + [ribbon_ addPointAt:location width:width_]; + lastLocation_ = location; + } + [ribbon_ update:delta]; +} + + +-(void)dealloc +{ + [super dealloc]; +} + +#pragma mark MotionStreak - CocosNodeTexture protocol + +-(void) setTexture:(CCTexture2D*) texture +{ + [ribbon_ setTexture: texture]; +} + +-(CCTexture2D*) texture +{ + return [ribbon_ texture]; +} + +-(ccBlendFunc) blendFunc +{ + return [ribbon_ blendFunc]; +} + +-(void) setBlendFunc:(ccBlendFunc)blendFunc +{ + [ribbon_ setBlendFunc:blendFunc]; +} + +@end diff --git a/libs/cocos2d/CCNode.h b/libs/cocos2d/CCNode.h new file mode 100644 index 0000000..786d427 --- /dev/null +++ b/libs/cocos2d/CCNode.h @@ -0,0 +1,510 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2009 Valentin Milea + * + * 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 + +#import "Platforms/CCGL.h" +#import "CCAction.h" +#import "ccTypes.h" +#import "CCTexture2D.h" +#import "CCProtocols.h" +#import "ccConfig.h" +#import "Support/CCArray.h" + +enum { + kCCNodeTagInvalid = -1, +}; + +@class CCCamera; +@class CCGridBase; + +/** CCNode is the main element. Anything thats gets drawn or contains things that get drawn is a CCNode. + The most popular CCNodes are: CCScene, CCLayer, CCSprite, CCMenu. + + The main features of a CCNode are: + - They can contain other CCNode nodes (addChild, getChildByTag, removeChild, etc) + - They can schedule periodic callback (schedule, unschedule, etc) + - They can execute actions (runAction, stopAction, etc) + + Some CCNode nodes provide extra functionality for them or their children. + + Subclassing a CCNode usually means (one/all) of: + - overriding init to initialize resources and schedule callbacks + - create callbacks to handle the advancement of time + - overriding draw to render the node + + Features of CCNode: + - position + - scale (x, y) + - rotation (in degrees, clockwise) + - CCCamera (an interface to gluLookAt ) + - CCGridBase (to do mesh transformations) + - anchor point + - size + - visible + - z-order + - openGL z position + + Default values: + - rotation: 0 + - position: (x=0,y=0) + - scale: (x=1,y=1) + - contentSize: (x=0,y=0) + - anchorPoint: (x=0,y=0) + + Limitations: + - A CCNode is a "void" object. It doesn't have a texture + + Order in transformations with grid disabled + -# The node will be translated (position) + -# The node will be rotated (rotation) + -# The node will be scaled (scale) + -# The node will be moved according to the camera values (camera) + + Order in transformations with grid enabled + -# The node will be translated (position) + -# The node will be rotated (rotation) + -# The node will be scaled (scale) + -# The grid will capture the screen + -# The node will be moved according to the camera values (camera) + -# The grid will render the captured screen + + Camera: + - Each node has a camera. By default it points to the center of the CCNode. + */ +@interface CCNode : NSObject +{ + // rotation angle + float rotation_; + + // scaling factors + float scaleX_, scaleY_; + + // position of the node + CGPoint position_; + CGPoint positionInPixels_; + + // is visible + BOOL visible_; + + // anchor point in pixels + CGPoint anchorPointInPixels_; + // anchor point normalized + CGPoint anchorPoint_; + // If YES the transformtions will be relative to (-transform.x, -transform.y). + // Sprites, Labels and any other "small" object uses it. + // Scenes, Layers and other "whole screen" object don't use it. + BOOL isRelativeAnchorPoint_; + + // untransformed size of the node + CGSize contentSize_; + CGSize contentSizeInPixels_; + + // transform + CGAffineTransform transform_, inverse_; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + GLfloat transformGL_[16]; +#endif + + // openGL real Z vertex + float vertexZ_; + + // a Camera + CCCamera *camera_; + + // a Grid + CCGridBase *grid_; + + // z-order value + NSInteger zOrder_; + + // array of children + CCArray *children_; + + // weakref to parent + CCNode *parent_; + + // a tag. any number you want to assign to the node + NSInteger tag_; + + // user data field + void *userData_; + + // Is running + BOOL isRunning_; + + // To reduce memory, place BOOLs that are not properties here: + BOOL isTransformDirty_:1; + BOOL isInverseDirty_:1; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + BOOL isTransformGLDirty_:1; +#endif +} + +/** The z order of the node relative to it's "brothers": children of the same parent */ +@property(nonatomic,readonly) NSInteger zOrder; +/** The real openGL Z vertex. + Differences between openGL Z vertex and cocos2d Z order: + - OpenGL Z modifies the Z vertex, and not the Z order in the relation between parent-children + - OpenGL Z might require to set 2D projection + - cocos2d Z order works OK if all the nodes uses the same openGL Z vertex. eg: vertexZ = 0 + @warning: Use it at your own risk since it might break the cocos2d parent-children z order + @since v0.8 + */ +@property (nonatomic,readwrite) float vertexZ; +/** The rotation (angle) of the node in degrees. 0 is the default rotation angle. Positive values rotate node CW. */ +@property(nonatomic,readwrite,assign) float rotation; +/** The scale factor of the node. 1.0 is the default scale factor. It modifies the X and Y scale at the same time. */ +@property(nonatomic,readwrite,assign) float scale; +/** The scale factor of the node. 1.0 is the default scale factor. It only modifies the X scale factor. */ +@property(nonatomic,readwrite,assign) float scaleX; +/** The scale factor of the node. 1.0 is the default scale factor. It only modifies the Y scale factor. */ +@property(nonatomic,readwrite,assign) float scaleY; +/** Position (x,y) of the node in points. (0,0) is the left-bottom corner. */ +@property(nonatomic,readwrite,assign) CGPoint position; +/** Position (x,y) of the node in points. (0,0) is the left-bottom corner. */ +@property(nonatomic,readwrite,assign) CGPoint positionInPixels; +/** A CCCamera object that lets you move the node using a gluLookAt +*/ +@property(nonatomic,readonly) CCCamera* camera; +/** Array of children */ +@property(nonatomic,readonly) CCArray *children; +/** A CCGrid object that is used when applying effects */ +@property(nonatomic,readwrite,retain) CCGridBase* grid; +/** Whether of not the node is visible. Default is YES */ +@property(nonatomic,readwrite,assign) BOOL visible; +/** anchorPoint is the point around which all transformations and positioning manipulations take place. + It's like a pin in the node where it is "attached" to its parent. + The anchorPoint is normalized, like a percentage. (0,0) means the bottom-left corner and (1,1) means the top-right corner. + But you can use values higher than (1,1) and lower than (0,0) too. + The default anchorPoint is (0,0). It starts in the bottom-left corner. CCSprite and other subclasses have a different default anchorPoint. + @since v0.8 + */ +@property(nonatomic,readwrite) CGPoint anchorPoint; +/** The anchorPoint in absolute pixels. + Since v0.8 you can only read it. If you wish to modify it, use anchorPoint instead + */ +@property(nonatomic,readonly) CGPoint anchorPointInPixels; + +/** The untransformed size of the node in Points + The contentSize remains the same no matter the node is scaled or rotated. + All nodes has a size. Layer and Scene has the same size of the screen. + @since v0.8 + */ +@property (nonatomic,readwrite) CGSize contentSize; + +/** The untransformed size of the node in Pixels + The contentSize remains the same no matter the node is scaled or rotated. + All nodes has a size. Layer and Scene has the same size of the screen. + @since v0.8 + */ +@property (nonatomic,readwrite) CGSize contentSizeInPixels; + +/** whether or not the node is running */ +@property(nonatomic,readonly) BOOL isRunning; +/** A weak reference to the parent */ +@property(nonatomic,readwrite,assign) CCNode* parent; +/** If YES the transformtions will be relative to it's anchor point. + * Sprites, Labels and any other sizeble object use it have it enabled by default. + * Scenes, Layers and other "whole screen" object don't use it, have it disabled by default. + */ +@property(nonatomic,readwrite,assign) BOOL isRelativeAnchorPoint; +/** A tag used to identify the node easily */ +@property(nonatomic,readwrite,assign) NSInteger tag; +/** A custom user data pointer */ +@property(nonatomic,readwrite,assign) void *userData; + +// initializators +/** allocates and initializes a node. + The node will be created as "autorelease". + */ ++(id) node; +/** initializes the node */ +-(id) init; + + +// scene managment + +/** callback that is called every time the CCNode enters the 'stage'. + If the CCNode enters the 'stage' with a transition, this callback is called when the transition starts. + During onEnter you can't a "sister/brother" node. + */ +-(void) onEnter; +/** callback that is called when the CCNode enters in the 'stage'. + If the CCNode enters the 'stage' with a transition, this callback is called when the transition finishes. + @since v0.8 + */ +-(void) onEnterTransitionDidFinish; +/** callback that is called every time the CCNode leaves the 'stage'. + If the CCNode leaves the 'stage' with a transition, this callback is called when the transition finishes. + During onExit you can't access a sibling node. + */ +-(void) onExit; + + +// composition: ADD + +/** Adds a child to the container with z-order as 0. + If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately. + @since v0.7.1 + */ +-(void) addChild: (CCNode*)node; + +/** Adds a child to the container with a z-order. + If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately. + @since v0.7.1 + */ +-(void) addChild: (CCNode*)node z:(NSInteger)z; + +/** Adds a child to the container with z order and tag. + If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately. + @since v0.7.1 + */ +-(void) addChild: (CCNode*)node z:(NSInteger)z tag:(NSInteger)tag; + +// composition: REMOVE + +/** Remove itself from its parent node. If cleanup is YES, then also remove all actions and callbacks. + If the node orphan, then nothing happens. + @since v0.99.3 + */ +-(void) removeFromParentAndCleanup:(BOOL)cleanup; + +/** Removes a child from the container. It will also cleanup all running actions depending on the cleanup parameter. + @since v0.7.1 + */ +-(void) removeChild: (CCNode*)node cleanup:(BOOL)cleanup; + +/** Removes a child from the container by tag value. It will also cleanup all running actions depending on the cleanup parameter + @since v0.7.1 + */ +-(void) removeChildByTag:(NSInteger) tag cleanup:(BOOL)cleanup; + +/** Removes all children from the container and do a cleanup all running actions depending on the cleanup parameter. + @since v0.7.1 + */ +-(void) removeAllChildrenWithCleanup:(BOOL)cleanup; + +// composition: GET +/** Gets a child from the container given its tag + @return returns a CCNode object + @since v0.7.1 + */ +-(CCNode*) getChildByTag:(NSInteger) tag; + +/** Reorders a child according to a new z value. + * The child MUST be already added. + */ +-(void) reorderChild:(CCNode*)child z:(NSInteger)zOrder; + +/** Stops all running actions and schedulers + @since v0.8 + */ +-(void) cleanup; + +// draw + +/** Override this method to draw your own node. + The following GL states will be enabled by default: + - glEnableClientState(GL_VERTEX_ARRAY); + - glEnableClientState(GL_COLOR_ARRAY); + - glEnableClientState(GL_TEXTURE_COORD_ARRAY); + - glEnable(GL_TEXTURE_2D); + + AND YOU SHOULD NOT DISABLE THEM AFTER DRAWING YOUR NODE + + But if you enable any other GL state, you should disable it after drawing your node. + */ +-(void) draw; +/** recursive method that visit its children and draw them */ +-(void) visit; + +// transformations + +/** performs OpenGL view-matrix transformation based on position, scale, rotation and other attributes. */ +-(void) transform; + +/** performs OpenGL view-matrix transformation of it's ancestors. + Generally the ancestors are already transformed, but in certain cases (eg: attaching a FBO) + it's necessary to transform the ancestors again. + @since v0.7.2 + */ +-(void) transformAncestors; + +/** returns a "local" axis aligned bounding box of the node in points. + The returned box is relative only to its parent. + The returned box is in Points. + + @since v0.8.2 + */ +- (CGRect) boundingBox; + +/** returns a "local" axis aligned bounding box of the node in pixels. + The returned box is relative only to its parent. + The returned box is in Points. + + @since v0.99.5 + */ +- (CGRect) boundingBoxInPixels; + + +// actions + +/** Executes an action, and returns the action that is executed. + The node becomes the action's target. + @warning Starting from v0.8 actions don't retain their target anymore. + @since v0.7.1 + @return An Action pointer + */ +-(CCAction*) runAction: (CCAction*) action; +/** Removes all actions from the running action list */ +-(void) stopAllActions; +/** Removes an action from the running action list */ +-(void) stopAction: (CCAction*) action; +/** Removes an action from the running action list given its tag + @since v0.7.1 +*/ +-(void) stopActionByTag:(NSInteger) tag; +/** Gets an action from the running action list given its tag + @since v0.7.1 + @return the Action the with the given tag + */ +-(CCAction*) getActionByTag:(NSInteger) tag; +/** Returns the numbers of actions that are running plus the ones that are schedule to run (actions in actionsToAdd and actions arrays). + * Composable actions are counted as 1 action. Example: + * If you are running 1 Sequence of 7 actions, it will return 1. + * If you are running 7 Sequences of 2 actions, it will return 7. + */ +-(NSUInteger) numberOfRunningActions; + +// timers + +/** check whether a selector is scheduled. */ +//-(BOOL) isScheduled: (SEL) selector; + +/** schedules the "update" method. It will use the order number 0. This method will be called every frame. + Scheduled methods with a lower order value will be called before the ones that have a higher order value. + Only one "udpate" method could be scheduled per node. + + @since v0.99.3 + */ +-(void) scheduleUpdate; + +/** schedules the "update" selector with a custom priority. This selector will be called every frame. + Scheduled selectors with a lower priority will be called before the ones that have a higher value. + Only one "udpate" selector could be scheduled per node (You can't have 2 'update' selectors). + + @since v0.99.3 + */ +-(void) scheduleUpdateWithPriority:(NSInteger)priority; + +/* unschedules the "update" method. + + @since v0.99.3 + */ +-(void) unscheduleUpdate; + + +/** schedules a selector. + The scheduled selector will be ticked every frame + */ +-(void) schedule: (SEL) s; +/** schedules a custom selector with an interval time in seconds. + If time is 0 it will be ticked every frame. + If time is 0, it is recommended to use 'scheduleUpdate' instead. + + If the selector is already scheduled, then the interval parameter will be updated without scheduling it again. + */ +-(void) schedule: (SEL) s interval:(ccTime)seconds; +/** unschedules a custom selector.*/ +-(void) unschedule: (SEL) s; + +/** unschedule all scheduled selectors: custom selectors, and the 'update' selector. + Actions are not affected by this method. +@since v0.99.3 + */ +-(void) unscheduleAllSelectors; + +/** resumes all scheduled selectors and actions. + Called internally by onEnter + */ +-(void) resumeSchedulerAndActions; +/** pauses all scheduled selectors and actions. + Called internally by onExit + */ +-(void) pauseSchedulerAndActions; + + +// transformation methods + +/** Returns the matrix that transform the node's (local) space coordinates into the parent's space coordinates. + The matrix is in Pixels. + @since v0.7.1 + */ +- (CGAffineTransform)nodeToParentTransform; +/** Returns the matrix that transform parent's space coordinates to the node's (local) space coordinates. + The matrix is in Pixels. + @since v0.7.1 + */ +- (CGAffineTransform)parentToNodeTransform; +/** Retrusn the world affine transform matrix. The matrix is in Pixels. + @since v0.7.1 + */ +- (CGAffineTransform)nodeToWorldTransform; +/** Returns the inverse world affine transform matrix. The matrix is in Pixels. + @since v0.7.1 + */ +- (CGAffineTransform)worldToNodeTransform; +/** Converts a Point to node (local) space coordinates. The result is in Points. + @since v0.7.1 + */ +- (CGPoint)convertToNodeSpace:(CGPoint)worldPoint; +/** Converts a Point to world space coordinates. The result is in Points. + @since v0.7.1 + */ +- (CGPoint)convertToWorldSpace:(CGPoint)nodePoint; +/** Converts a Point to node (local) space coordinates. The result is in Points. + treating the returned/received node point as anchor relative. + @since v0.7.1 + */ +- (CGPoint)convertToNodeSpaceAR:(CGPoint)worldPoint; +/** Converts a local Point to world space coordinates.The result is in Points. + treating the returned/received node point as anchor relative. + @since v0.7.1 + */ +- (CGPoint)convertToWorldSpaceAR:(CGPoint)nodePoint; + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +/** Converts a UITouch to node (local) space coordinates. The result is in Points. + @since v0.7.1 + */ +- (CGPoint)convertTouchToNodeSpace:(UITouch *)touch; +/** Converts a UITouch to node (local) space coordinates. The result is in Points. + This method is AR (Anchor Relative).. + @since v0.7.1 + */ +- (CGPoint)convertTouchToNodeSpaceAR:(UITouch *)touch; +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED +@end diff --git a/libs/cocos2d/CCNode.m b/libs/cocos2d/CCNode.m new file mode 100644 index 0000000..e9a571b --- /dev/null +++ b/libs/cocos2d/CCNode.m @@ -0,0 +1,883 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2009 Valentin Milea + * + * 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 position = position_, positionInPixels = positionInPixels_; +@synthesize anchorPoint = anchorPoint_, anchorPointInPixels = anchorPointInPixels_; +@synthesize contentSize = contentSize_, contentSizeInPixels = contentSizeInPixels_; +@synthesize isRelativeAnchorPoint = isRelativeAnchorPoint_; + +// getters synthesized, setters explicit +-(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) setIsRelativeAnchorPoint: (BOOL)newValue +{ + isRelativeAnchorPoint_ = newValue; + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif +} + +-(void) setAnchorPoint:(CGPoint)point +{ + if( ! CGPointEqualToPoint(point, anchorPoint_) ) { + anchorPoint_ = point; + anchorPointInPixels_ = ccp( contentSizeInPixels_.width * anchorPoint_.x, contentSizeInPixels_.height * anchorPoint_.y ); + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif + } +} + +-(void) setContentSize:(CGSize)size +{ + if( ! CGSizeEqualToSize(size, contentSize_) ) { + contentSize_ = size; + + if( CC_CONTENT_SCALE_FACTOR() == 1 ) + contentSizeInPixels_ = contentSize_; + else + contentSizeInPixels_ = CGSizeMake( size.width * CC_CONTENT_SCALE_FACTOR(), size.height * CC_CONTENT_SCALE_FACTOR() ); + + anchorPointInPixels_ = ccp( contentSizeInPixels_.width * anchorPoint_.x, contentSizeInPixels_.height * anchorPoint_.y ); + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif + } +} + +-(void) setContentSizeInPixels:(CGSize)size +{ + if( ! CGSizeEqualToSize(size, contentSizeInPixels_) ) { + contentSizeInPixels_ = size; + + if( CC_CONTENT_SCALE_FACTOR() == 1 ) + contentSize_ = contentSizeInPixels_; + else + contentSize_ = CGSizeMake( size.width / CC_CONTENT_SCALE_FACTOR(), size.height / CC_CONTENT_SCALE_FACTOR() ); + + anchorPointInPixels_ = ccp( contentSizeInPixels_.width * anchorPoint_.x, contentSizeInPixels_.height * anchorPoint_.y ); + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif + } +} + +- (CGRect) boundingBox +{ + CGRect ret = [self boundingBoxInPixels]; + return CC_RECT_PIXELS_TO_POINTS( ret ); +} + +- (CGRect) boundingBoxInPixels +{ + CGRect rect = CGRectMake(0, 0, contentSizeInPixels_.width, contentSizeInPixels_.height); + return CGRectApplyAffineTransform(rect, [self nodeToParentTransform]); +} + +-(void) setVertexZ:(float)vertexZ +{ + vertexZ_ = vertexZ * CC_CONTENT_SCALE_FACTOR(); +} + +-(float) scale +{ + NSAssert( scaleX_ == scaleY_, @"CCNode#scale. ScaleX != ScaleY. Don't know which one to return"); + return scaleX_; +} + +-(void) setScale:(float) s +{ + scaleX_ = scaleY_ = s; + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif +} + +#pragma mark CCNode - Init & cleanup + ++(id) node +{ + return [[[self alloc] init] autorelease]; +} + +-(id) init +{ + if ((self=[super init]) ) { + + isRunning_ = NO; + + rotation_ = 0.0f; + scaleX_ = scaleY_ = 1.0f; + positionInPixels_ = position_ = CGPointZero; + anchorPointInPixels_ = anchorPoint_ = CGPointZero; + contentSizeInPixels_ = contentSize_ = CGSizeZero; + + + // "whole screen" objects. like Scenes and Layers, should set isRelativeAnchorPoint to NO + isRelativeAnchorPoint_ = YES; + + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif + + vertexZ_ = 0; + + grid_ = nil; + + visible_ = YES; + + tag_ = kCCNodeTagInvalid; + + zOrder_ = 0; + + // lazy alloc + camera_ = nil; + + // children (lazy allocs) + children_ = nil; + + // userData is always inited as nil + userData_ = nil; + + //initialize parent to nil + parent_ = nil; + } + + return self; +} + +- (void)cleanup +{ + // actions + [self stopAllActions]; + [self unscheduleAllSelectors]; + + // timers + [children_ makeObjectsPerformSelector:@selector(cleanup)]; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag_]; +} + +- (void) dealloc +{ + CCLOGINFO( @"cocos2d: deallocing %@", self); + + // attributes + [camera_ release]; + + [grid_ release]; + + // children + CCNode *child; + CCARRAY_FOREACH(children_, child) + child.parent = nil; + + [children_ release]; + + [super dealloc]; +} + +#pragma mark CCNode Composition + +-(void) childrenAlloc +{ + children_ = [[CCArray alloc] initWithCapacity:4]; +} + +// camera: lazy alloc +-(CCCamera*) camera +{ + if( ! camera_ ) { + camera_ = [[CCCamera alloc] init]; + + // by default, center camera at the Sprite's anchor point + // [camera_ setCenterX:anchorPointInPixels_.x centerY:anchorPointInPixels_.y centerZ:0]; + // [camera_ setEyeX:anchorPointInPixels_.x eyeY:anchorPointInPixels_.y eyeZ:1]; + + // [camera_ setCenterX:0 centerY:0 centerZ:0]; + // [camera_ setEyeX:0 eyeY:0 eyeZ:1]; + + } + + return camera_; +} + +-(CCNode*) getChildByTag:(NSInteger) aTag +{ + NSAssert( aTag != kCCNodeTagInvalid, @"Invalid tag"); + + CCNode *node; + CCARRAY_FOREACH(children_, node){ + if( node.tag == aTag ) + return node; + } + // not found + return nil; +} + +/* "add" logic MUST only be on this method + * If a class want's to extend the 'addChild' behaviour it only needs + * to override this method + */ +-(void) addChild: (CCNode*) child z:(NSInteger)z tag:(NSInteger) aTag +{ + NSAssert( child != nil, @"Argument must be non-nil"); + NSAssert( child.parent == nil, @"child already added. It can't be added again"); + + if( ! children_ ) + [self childrenAlloc]; + + [self insertChild:child z:z]; + + child.tag = aTag; + + [child setParent: self]; + + if( isRunning_ ) { + [child onEnter]; + [child onEnterTransitionDidFinish]; + } +} + +-(void) addChild: (CCNode*) child z:(NSInteger)z +{ + NSAssert( child != nil, @"Argument must be non-nil"); + [self addChild:child z:z tag:child.tag]; +} + +-(void) addChild: (CCNode*) child +{ + NSAssert( child != nil, @"Argument must be non-nil"); + [self addChild:child z:child.zOrder tag:child.tag]; +} + +-(void) removeFromParentAndCleanup:(BOOL)cleanup +{ + [parent_ removeChild:self cleanup:cleanup]; +} + +/* "remove" logic MUST only be on this method + * If a class want's to extend the 'removeChild' behavior it only needs + * to override this method + */ +-(void) removeChild: (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 ); + + // 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( ! (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 diff --git a/libs/cocos2d/CCParallaxNode.h b/libs/cocos2d/CCParallaxNode.h new file mode 100644 index 0000000..9a5106d --- /dev/null +++ b/libs/cocos2d/CCParallaxNode.h @@ -0,0 +1,49 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009-2010 Ricardo Quesada + * + * 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 "Support/ccCArray.h" + +/** CCParallaxNode: A node that simulates a parallax scroller + + The children will be moved faster / slower than the parent according the the parallax ratio. + + */ +@interface CCParallaxNode : CCNode +{ + ccArray *parallaxArray_; + CGPoint lastPosition; +} + +/** array that holds the offset / ratio of the children */ +@property (nonatomic,readwrite) ccArray * parallaxArray; + +/** Adds a child to the container with a z-order, a parallax ratio and a position offset + It returns self, so you can chain several addChilds. + @since v0.8 + */ +-(void) addChild: (CCNode*)node z:(NSInteger)z parallaxRatio:(CGPoint)c positionOffset:(CGPoint)positionOffset; + +@end diff --git a/libs/cocos2d/CCParallaxNode.m b/libs/cocos2d/CCParallaxNode.m new file mode 100644 index 0000000..9f06911 --- /dev/null +++ b/libs/cocos2d/CCParallaxNode.m @@ -0,0 +1,160 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009-2010 Ricardo Quesada + * + * 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 "CCParallaxNode.h" +#import "Support/CGPointExtension.h" +#import "Support/ccCArray.h" + +@interface CGPointObject : NSObject +{ + CGPoint ratio_; + CGPoint offset_; + CCNode *child_; // weak ref +} +@property (readwrite) CGPoint ratio; +@property (readwrite) CGPoint offset; +@property (readwrite,assign) CCNode *child; ++(id) pointWithCGPoint:(CGPoint)point offset:(CGPoint)offset; +-(id) initWithCGPoint:(CGPoint)point offset:(CGPoint)offset; +@end +@implementation CGPointObject +@synthesize ratio = ratio_; +@synthesize offset = offset_; +@synthesize child=child_; + ++(id) pointWithCGPoint:(CGPoint)ratio offset:(CGPoint)offset +{ + return [[[self alloc] initWithCGPoint:ratio offset:offset] autorelease]; +} +-(id) initWithCGPoint:(CGPoint)ratio offset:(CGPoint)offset +{ + if( (self=[super init])) { + ratio_ = ratio; + offset_ = offset; + } + return self; +} +@end + +@implementation CCParallaxNode + +@synthesize parallaxArray = parallaxArray_; + +-(id) init +{ + if( (self=[super init]) ) { + parallaxArray_ = ccArrayNew(5); + lastPosition = CGPointMake(-100,-100); + } + return self; +} + +- (void) dealloc +{ + if( parallaxArray_ ) { + ccArrayFree(parallaxArray_); + parallaxArray_ = nil; + } + [super dealloc]; +} + +-(void) addChild:(CCNode*)child z:(NSInteger)z tag:(NSInteger)tag +{ + NSAssert(NO,@"ParallaxNode: use addChild:z:parallaxRatio:positionOffset instead"); +} + +-(void) addChild: (CCNode*) child z:(NSInteger)z parallaxRatio:(CGPoint)ratio positionOffset:(CGPoint)offset +{ + NSAssert( child != nil, @"Argument must be non-nil"); + CGPointObject *obj = [CGPointObject pointWithCGPoint:ratio offset:offset]; + obj.child = child; + ccArrayAppendObjectWithResize(parallaxArray_, obj); + + CGPoint pos = self.position; + pos.x = pos.x * ratio.x + offset.x; + pos.y = pos.y * ratio.y + offset.y; + child.position = pos; + + [super addChild: child z:z tag:child.tag]; +} + +-(void) removeChild:(CCNode*)node cleanup:(BOOL)cleanup +{ + for( unsigned int i=0;i < parallaxArray_->num;i++) { + CGPointObject *point = parallaxArray_->arr[i]; + if( [point.child isEqual:node] ) { + ccArrayRemoveObjectAtIndex(parallaxArray_, i); + break; + } + } + [super removeChild:node cleanup:cleanup]; +} + +-(void) removeAllChildrenWithCleanup:(BOOL)cleanup +{ + ccArrayRemoveAllObjects(parallaxArray_); + [super removeAllChildrenWithCleanup:cleanup]; +} + +-(CGPoint) absolutePosition_ +{ + CGPoint ret = position_; + + CCNode *cn = self; + + while (cn.parent != nil) { + cn = cn.parent; + ret = ccpAdd( ret, cn.position ); + } + + return ret; +} + +/* + The positions are updated at visit because: + - using a timer is not guaranteed that it will called after all the positions were updated + - overriding "draw" will only precise if the children have a z > 0 +*/ +-(void) visit +{ +// CGPoint pos = position_; +// CGPoint pos = [self convertToWorldSpace:CGPointZero]; + CGPoint pos = [self absolutePosition_]; + if( ! CGPointEqualToPoint(pos, lastPosition) ) { + + for(unsigned int i=0; i < parallaxArray_->num; i++ ) { + + CGPointObject *point = parallaxArray_->arr[i]; + float x = -pos.x + pos.x * point.ratio.x + point.offset.x; + float y = -pos.y + pos.y * point.ratio.y + point.offset.y; + point.child.position = ccp(x,y); + } + + lastPosition = pos; + } + + [super visit]; +} +@end diff --git a/libs/cocos2d/CCParticleExamples.h b/libs/cocos2d/CCParticleExamples.h new file mode 100644 index 0000000..b28fca9 --- /dev/null +++ b/libs/cocos2d/CCParticleExamples.h @@ -0,0 +1,110 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 + +#import "CCParticleSystemPoint.h" +#import "CCParticleSystemQuad.h" + +// build each architecture with the optimal particle system + +// ARMv7, Mac or Simulator use "Quad" particle +#if defined(__ARM_NEON__) || defined(__MAC_OS_X_VERSION_MAX_ALLOWED) || TARGET_IPHONE_SIMULATOR + #define ARCH_OPTIMAL_PARTICLE_SYSTEM CCParticleSystemQuad + +// ARMv6 use "Point" particle +#elif __arm__ + #define ARCH_OPTIMAL_PARTICLE_SYSTEM CCParticleSystemPoint +#else + #error(unknown architecture) +#endif + + +//! A fire particle system +@interface CCParticleFire: ARCH_OPTIMAL_PARTICLE_SYSTEM +{ +} +@end + +//! A fireworks particle system +@interface CCParticleFireworks : ARCH_OPTIMAL_PARTICLE_SYSTEM +{ +} +@end + +//! A sun particle system +@interface CCParticleSun : ARCH_OPTIMAL_PARTICLE_SYSTEM +{ +} +@end + +//! A galaxy particle system +@interface CCParticleGalaxy : ARCH_OPTIMAL_PARTICLE_SYSTEM +{ +} +@end + +//! A flower particle system +@interface CCParticleFlower : ARCH_OPTIMAL_PARTICLE_SYSTEM +{ +} +@end + +//! A meteor particle system +@interface CCParticleMeteor : ARCH_OPTIMAL_PARTICLE_SYSTEM +{ +} +@end + +//! An spiral particle system +@interface CCParticleSpiral : ARCH_OPTIMAL_PARTICLE_SYSTEM +{ +} +@end + +//! An explosion particle system +@interface CCParticleExplosion : ARCH_OPTIMAL_PARTICLE_SYSTEM +{ +} +@end + +//! An smoke particle system +@interface CCParticleSmoke : ARCH_OPTIMAL_PARTICLE_SYSTEM +{ +} +@end + +//! An snow particle system +@interface CCParticleSnow : ARCH_OPTIMAL_PARTICLE_SYSTEM +{ +} +@end + +//! A rain particle system +@interface CCParticleRain : ARCH_OPTIMAL_PARTICLE_SYSTEM +{ +} +@end diff --git a/libs/cocos2d/CCParticleExamples.m b/libs/cocos2d/CCParticleExamples.m new file mode 100644 index 0000000..4fff57f --- /dev/null +++ b/libs/cocos2d/CCParticleExamples.m @@ -0,0 +1,925 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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. + * + */ + + +// cocos2d +#import "CCParticleExamples.h" +#import "CCTextureCache.h" +#import "CCDirector.h" +#import "Support/CGPointExtension.h" + +// +// ParticleFireworks +// +@implementation CCParticleFireworks +-(id) init +{ + return [self initWithTotalParticles:1500]; +} + +-(id) initWithTotalParticles:(NSUInteger)p +{ + if( (self=[super initWithTotalParticles:p]) ) { + // duration + duration = kCCParticleDurationInfinity; + + // Gravity Mode + self.emitterMode = kCCParticleModeGravity; + + // Gravity Mode: gravity + self.gravity = ccp(0,-90); + + // Gravity Mode: radial + self.radialAccel = 0; + self.radialAccelVar = 0; + + // Gravity Mode: speed of particles + self.speed = 180; + self.speedVar = 50; + + // emitter position + CGSize winSize = [[CCDirector sharedDirector] winSize]; + self.position = ccp(winSize.width/2, winSize.height/2); + + // angle + angle = 90; + angleVar = 20; + + // life of particles + life = 3.5f; + lifeVar = 1; + + // emits per frame + emissionRate = totalParticles/life; + + // color of particles + startColor.r = 0.5f; + startColor.g = 0.5f; + startColor.b = 0.5f; + startColor.a = 1.0f; + startColorVar.r = 0.5f; + startColorVar.g = 0.5f; + startColorVar.b = 0.5f; + startColorVar.a = 0.1f; + endColor.r = 0.1f; + endColor.g = 0.1f; + endColor.b = 0.1f; + endColor.a = 0.2f; + endColorVar.r = 0.1f; + endColorVar.g = 0.1f; + endColorVar.b = 0.1f; + endColorVar.a = 0.2f; + + // size, in pixels + startSize = 8.0f; + startSizeVar = 2.0f; + endSize = kCCParticleStartSizeEqualToEndSize; + + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; + + // additive + self.blendAdditive = NO; + } + + return self; +} +@end + +// +// ParticleFire +// +@implementation CCParticleFire +-(id) init +{ + return [self initWithTotalParticles:250]; +} + +-(id) initWithTotalParticles:(NSUInteger) p +{ + if( (self=[super initWithTotalParticles:p]) ) { + + // duration + duration = kCCParticleDurationInfinity; + + // Gravity Mode + self.emitterMode = kCCParticleModeGravity; + + // Gravity Mode: gravity + self.gravity = ccp(0,0); + + // Gravity Mode: radial acceleration + self.radialAccel = 0; + self.radialAccelVar = 0; + + // Gravity Mode: speed of particles + self.speed = 60; + self.speedVar = 20; + + // starting angle + angle = 90; + angleVar = 10; + + // emitter position + CGSize winSize = [[CCDirector sharedDirector] winSize]; + self.position = ccp(winSize.width/2, 60); + posVar = ccp(40, 20); + + // life of particles + life = 3; + lifeVar = 0.25f; + + + // size, in pixels + startSize = 54.0f; + startSizeVar = 10.0f; + endSize = kCCParticleStartSizeEqualToEndSize; + + // emits per frame + emissionRate = totalParticles/life; + + // color of particles + startColor.r = 0.76f; + startColor.g = 0.25f; + startColor.b = 0.12f; + startColor.a = 1.0f; + startColorVar.r = 0.0f; + startColorVar.g = 0.0f; + startColorVar.b = 0.0f; + startColorVar.a = 0.0f; + endColor.r = 0.0f; + endColor.g = 0.0f; + endColor.b = 0.0f; + endColor.a = 1.0f; + endColorVar.r = 0.0f; + endColorVar.g = 0.0f; + endColorVar.b = 0.0f; + endColorVar.a = 0.0f; + + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; + + // additive + self.blendAdditive = YES; + } + + return self; +} +@end + +// +// ParticleSun +// +@implementation CCParticleSun +-(id) init +{ + return [self initWithTotalParticles:350]; +} + +-(id) initWithTotalParticles:(NSUInteger) p +{ + if( (self=[super initWithTotalParticles:p]) ) { + + // additive + self.blendAdditive = YES; + + // duration + duration = kCCParticleDurationInfinity; + + // Gravity Mode + self.emitterMode = kCCParticleModeGravity; + + // Gravity Mode: gravity + self.gravity = ccp(0,0); + + // Gravity mode: radial acceleration + self.radialAccel = 0; + self.radialAccelVar = 0; + + // Gravity mode: speed of particles + self.speed = 20; + self.speedVar = 5; + + + // angle + angle = 90; + angleVar = 360; + + // emitter position + CGSize winSize = [[CCDirector sharedDirector] winSize]; + self.position = ccp(winSize.width/2, winSize.height/2); + posVar = CGPointZero; + + // life of particles + life = 1; + lifeVar = 0.5f; + + // size, in pixels + startSize = 30.0f; + startSizeVar = 10.0f; + endSize = kCCParticleStartSizeEqualToEndSize; + + // emits per seconds + emissionRate = totalParticles/life; + + // color of particles + startColor.r = 0.76f; + startColor.g = 0.25f; + startColor.b = 0.12f; + startColor.a = 1.0f; + startColorVar.r = 0.0f; + startColorVar.g = 0.0f; + startColorVar.b = 0.0f; + startColorVar.a = 0.0f; + endColor.r = 0.0f; + endColor.g = 0.0f; + endColor.b = 0.0f; + endColor.a = 1.0f; + endColorVar.r = 0.0f; + endColorVar.g = 0.0f; + endColorVar.b = 0.0f; + endColorVar.a = 0.0f; + + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; + } + + return self; +} +@end + +// +// ParticleGalaxy +// +@implementation CCParticleGalaxy +-(id) init +{ + return [self initWithTotalParticles:200]; +} + +-(id) initWithTotalParticles:(NSUInteger)p +{ + if( (self=[super initWithTotalParticles:p]) ) { + + // duration + duration = kCCParticleDurationInfinity; + + // Gravity Mode + self.emitterMode = kCCParticleModeGravity; + + // Gravity Mode: gravity + self.gravity = ccp(0,0); + + // Gravity Mode: speed of particles + self.speed = 60; + self.speedVar = 10; + + // Gravity Mode: radial + self.radialAccel = -80; + self.radialAccelVar = 0; + + // Gravity Mode: tagential + self.tangentialAccel = 80; + self.tangentialAccelVar = 0; + + // angle + angle = 90; + angleVar = 360; + + // emitter position + CGSize winSize = [[CCDirector sharedDirector] winSize]; + self.position = ccp(winSize.width/2, winSize.height/2); + posVar = CGPointZero; + + // life of particles + life = 4; + lifeVar = 1; + + // size, in pixels + startSize = 37.0f; + startSizeVar = 10.0f; + endSize = kCCParticleStartSizeEqualToEndSize; + + // emits per second + emissionRate = totalParticles/life; + + // color of particles + startColor.r = 0.12f; + startColor.g = 0.25f; + startColor.b = 0.76f; + startColor.a = 1.0f; + startColorVar.r = 0.0f; + startColorVar.g = 0.0f; + startColorVar.b = 0.0f; + startColorVar.a = 0.0f; + endColor.r = 0.0f; + endColor.g = 0.0f; + endColor.b = 0.0f; + endColor.a = 1.0f; + endColorVar.r = 0.0f; + endColorVar.g = 0.0f; + endColorVar.b = 0.0f; + endColorVar.a = 0.0f; + + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; + + // additive + self.blendAdditive = YES; + } + + return self; +} +@end + +// +// ParticleFlower +// +@implementation CCParticleFlower +-(id) init +{ + return [self initWithTotalParticles:250]; +} + +-(id) initWithTotalParticles:(NSUInteger) p +{ + if( (self=[super initWithTotalParticles:p]) ) { + + // duration + duration = kCCParticleDurationInfinity; + + // Gravity Mode + self.emitterMode = kCCParticleModeGravity; + + // Gravity Mode: gravity + self.gravity = ccp(0,0); + + // Gravity Mode: speed of particles + self.speed = 80; + self.speedVar = 10; + + // Gravity Mode: radial + self.radialAccel = -60; + self.radialAccelVar = 0; + + // Gravity Mode: tagential + self.tangentialAccel = 15; + self.tangentialAccelVar = 0; + + // angle + angle = 90; + angleVar = 360; + + // emitter position + CGSize winSize = [[CCDirector sharedDirector] winSize]; + self.position = ccp(winSize.width/2, winSize.height/2); + posVar = CGPointZero; + + // life of particles + life = 4; + lifeVar = 1; + + // size, in pixels + startSize = 30.0f; + startSizeVar = 10.0f; + endSize = kCCParticleStartSizeEqualToEndSize; + + // emits per second + emissionRate = totalParticles/life; + + // color of particles + startColor.r = 0.50f; + startColor.g = 0.50f; + startColor.b = 0.50f; + startColor.a = 1.0f; + startColorVar.r = 0.5f; + startColorVar.g = 0.5f; + startColorVar.b = 0.5f; + startColorVar.a = 0.5f; + endColor.r = 0.0f; + endColor.g = 0.0f; + endColor.b = 0.0f; + endColor.a = 1.0f; + endColorVar.r = 0.0f; + endColorVar.g = 0.0f; + endColorVar.b = 0.0f; + endColorVar.a = 0.0f; + + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; + + // additive + self.blendAdditive = YES; + } + + return self; +} +@end + +// +// ParticleMeteor +// +@implementation CCParticleMeteor +-(id) init +{ + return [self initWithTotalParticles:150]; +} + +-(id) initWithTotalParticles:(NSUInteger) p +{ + if( (self=[super initWithTotalParticles:p]) ) { + + // duration + duration = kCCParticleDurationInfinity; + + // Gravity Mode + self.emitterMode = kCCParticleModeGravity; + + // Gravity Mode: gravity + self.gravity = ccp(-200,200); + + // Gravity Mode: speed of particles + self.speed = 15; + self.speedVar = 5; + + // Gravity Mode: radial + self.radialAccel = 0; + self.radialAccelVar = 0; + + // Gravity Mode: tagential + self.tangentialAccel = 0; + self.tangentialAccelVar = 0; + + // angle + angle = 90; + angleVar = 360; + + // emitter position + CGSize winSize = [[CCDirector sharedDirector] winSize]; + self.position = ccp(winSize.width/2, winSize.height/2); + posVar = CGPointZero; + + // life of particles + life = 2; + lifeVar = 1; + + // size, in pixels + startSize = 60.0f; + startSizeVar = 10.0f; + endSize = kCCParticleStartSizeEqualToEndSize; + + // emits per second + emissionRate = totalParticles/life; + + // color of particles + startColor.r = 0.2f; + startColor.g = 0.4f; + startColor.b = 0.7f; + startColor.a = 1.0f; + startColorVar.r = 0.0f; + startColorVar.g = 0.0f; + startColorVar.b = 0.2f; + startColorVar.a = 0.1f; + endColor.r = 0.0f; + endColor.g = 0.0f; + endColor.b = 0.0f; + endColor.a = 1.0f; + endColorVar.r = 0.0f; + endColorVar.g = 0.0f; + endColorVar.b = 0.0f; + endColorVar.a = 0.0f; + + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; + + // additive + self.blendAdditive = YES; + } + + return self; +} +@end + +// +// ParticleSpiral +// +@implementation CCParticleSpiral +-(id) init +{ + return [self initWithTotalParticles:500]; +} + +-(id) initWithTotalParticles:(NSUInteger) p +{ + if( (self=[super initWithTotalParticles:p]) ) { + + // duration + duration = kCCParticleDurationInfinity; + + // Gravity Mode + self.emitterMode = kCCParticleModeGravity; + + // Gravity Mode: gravity + self.gravity = ccp(0,0); + + // Gravity Mode: speed of particles + self.speed = 150; + self.speedVar = 0; + + // Gravity Mode: radial + self.radialAccel = -380; + self.radialAccelVar = 0; + + // Gravity Mode: tagential + self.tangentialAccel = 45; + self.tangentialAccelVar = 0; + + // angle + angle = 90; + angleVar = 0; + + // emitter position + CGSize winSize = [[CCDirector sharedDirector] winSize]; + self.position = ccp(winSize.width/2, winSize.height/2); + posVar = CGPointZero; + + // life of particles + life = 12; + lifeVar = 0; + + // size, in pixels + startSize = 20.0f; + startSizeVar = 0.0f; + endSize = kCCParticleStartSizeEqualToEndSize; + + // emits per second + emissionRate = totalParticles/life; + + // color of particles + startColor.r = 0.5f; + startColor.g = 0.5f; + startColor.b = 0.5f; + startColor.a = 1.0f; + startColorVar.r = 0.5f; + startColorVar.g = 0.5f; + startColorVar.b = 0.5f; + startColorVar.a = 0.0f; + endColor.r = 0.5f; + endColor.g = 0.5f; + endColor.b = 0.5f; + endColor.a = 1.0f; + endColorVar.r = 0.5f; + endColorVar.g = 0.5f; + endColorVar.b = 0.5f; + endColorVar.a = 0.0f; + + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; + + // additive + self.blendAdditive = NO; + } + + return self; +} +@end + +// +// ParticleExplosion +// +@implementation CCParticleExplosion +-(id) init +{ + return [self initWithTotalParticles:700]; +} + +-(id) initWithTotalParticles:(NSUInteger)p +{ + if( (self=[super initWithTotalParticles:p]) ) { + + // duration + duration = 0.1f; + + self.emitterMode = kCCParticleModeGravity; + + // Gravity Mode: gravity + self.gravity = ccp(0,0); + + // Gravity Mode: speed of particles + self.speed = 70; + self.speedVar = 40; + + // Gravity Mode: radial + self.radialAccel = 0; + self.radialAccelVar = 0; + + // Gravity Mode: tagential + self.tangentialAccel = 0; + self.tangentialAccelVar = 0; + + // angle + angle = 90; + angleVar = 360; + + // emitter position + CGSize winSize = [[CCDirector sharedDirector] winSize]; + self.position = ccp(winSize.width/2, winSize.height/2); + posVar = CGPointZero; + + // life of particles + life = 5.0f; + lifeVar = 2; + + // size, in pixels + startSize = 15.0f; + startSizeVar = 10.0f; + endSize = kCCParticleStartSizeEqualToEndSize; + + // emits per second + emissionRate = totalParticles/duration; + + // color of particles + startColor.r = 0.7f; + startColor.g = 0.1f; + startColor.b = 0.2f; + startColor.a = 1.0f; + startColorVar.r = 0.5f; + startColorVar.g = 0.5f; + startColorVar.b = 0.5f; + startColorVar.a = 0.0f; + endColor.r = 0.5f; + endColor.g = 0.5f; + endColor.b = 0.5f; + endColor.a = 0.0f; + endColorVar.r = 0.5f; + endColorVar.g = 0.5f; + endColorVar.b = 0.5f; + endColorVar.a = 0.0f; + + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; + + // additive + self.blendAdditive = NO; + } + + return self; +} +@end + +// +// ParticleSmoke +// +@implementation CCParticleSmoke +-(id) init +{ + return [self initWithTotalParticles:200]; +} + +-(id) initWithTotalParticles:(NSUInteger) p +{ + if( (self=[super initWithTotalParticles:p]) ) { + + // duration + duration = kCCParticleDurationInfinity; + + // Emitter mode: Gravity Mode + self.emitterMode = kCCParticleModeGravity; + + // Gravity Mode: gravity + self.gravity = ccp(0,0); + + // Gravity Mode: radial acceleration + self.radialAccel = 0; + self.radialAccelVar = 0; + + // Gravity Mode: speed of particles + self.speed = 25; + self.speedVar = 10; + + // angle + angle = 90; + angleVar = 5; + + // emitter position + CGSize winSize = [[CCDirector sharedDirector] winSize]; + self.position = ccp(winSize.width/2, 0); + posVar = ccp(20, 0); + + // life of particles + life = 4; + lifeVar = 1; + + // size, in pixels + startSize = 60.0f; + startSizeVar = 10.0f; + endSize = kCCParticleStartSizeEqualToEndSize; + + // emits per frame + emissionRate = totalParticles/life; + + // color of particles + startColor.r = 0.8f; + startColor.g = 0.8f; + startColor.b = 0.8f; + startColor.a = 1.0f; + startColorVar.r = 0.02f; + startColorVar.g = 0.02f; + startColorVar.b = 0.02f; + startColorVar.a = 0.0f; + endColor.r = 0.0f; + endColor.g = 0.0f; + endColor.b = 0.0f; + endColor.a = 1.0f; + endColorVar.r = 0.0f; + endColorVar.g = 0.0f; + endColorVar.b = 0.0f; + endColorVar.a = 0.0f; + + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; + + // additive + self.blendAdditive = NO; + } + + return self; +} +@end + +@implementation CCParticleSnow +-(id) init +{ + return [self initWithTotalParticles:700]; +} + +-(id) initWithTotalParticles:(NSUInteger)p +{ + if( (self=[super initWithTotalParticles:p]) ) { + + // duration + duration = kCCParticleDurationInfinity; + + // set gravity mode. + self.emitterMode = kCCParticleModeGravity; + + // Gravity Mode: gravity + self.gravity = ccp(0,-1); + + // Gravity Mode: speed of particles + self.speed = 5; + self.speedVar = 1; + + // Gravity Mode: radial + self.radialAccel = 0; + self.radialAccelVar = 1; + + // Gravity mode: tagential + self.tangentialAccel = 0; + self.tangentialAccelVar = 1; + + // emitter position + self.position = (CGPoint) { + [[CCDirector sharedDirector] winSize].width / 2, + [[CCDirector sharedDirector] winSize].height + 10 + }; + posVar = ccp( [[CCDirector sharedDirector] winSize].width / 2, 0 ); + + // angle + angle = -90; + angleVar = 5; + + // life of particles + life = 45; + lifeVar = 15; + + // size, in pixels + startSize = 10.0f; + startSizeVar = 5.0f; + endSize = kCCParticleStartSizeEqualToEndSize; + + // emits per second + emissionRate = 10; + + // color of particles + startColor.r = 1.0f; + startColor.g = 1.0f; + startColor.b = 1.0f; + startColor.a = 1.0f; + startColorVar.r = 0.0f; + startColorVar.g = 0.0f; + startColorVar.b = 0.0f; + startColorVar.a = 0.0f; + endColor.r = 1.0f; + endColor.g = 1.0f; + endColor.b = 1.0f; + endColor.a = 0.0f; + endColorVar.r = 0.0f; + endColorVar.g = 0.0f; + endColorVar.b = 0.0f; + endColorVar.a = 0.0f; + + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; + + // additive + self.blendAdditive = NO; + } + + return self; +} +@end + +@implementation CCParticleRain +-(id) init +{ + return [self initWithTotalParticles:1000]; +} + +-(id) initWithTotalParticles:(NSUInteger)p +{ + if( (self=[super initWithTotalParticles:p]) ) { + + // duration + duration = kCCParticleDurationInfinity; + + self.emitterMode = kCCParticleModeGravity; + + // Gravity Mode: gravity + self.gravity = ccp(10,-10); + + // Gravity Mode: radial + self.radialAccel = 0; + self.radialAccelVar = 1; + + // Gravity Mode: tagential + self.tangentialAccel = 0; + self.tangentialAccelVar = 1; + + // Gravity Mode: speed of particles + self.speed = 130; + self.speedVar = 30; + + // angle + angle = -90; + angleVar = 5; + + + // emitter position + self.position = (CGPoint) { + [[CCDirector sharedDirector] winSize].width / 2, + [[CCDirector sharedDirector] winSize].height + }; + posVar = ccp( [[CCDirector sharedDirector] winSize].width / 2, 0 ); + + // life of particles + life = 4.5f; + lifeVar = 0; + + // size, in pixels + startSize = 4.0f; + startSizeVar = 2.0f; + endSize = kCCParticleStartSizeEqualToEndSize; + + // emits per second + emissionRate = 20; + + // color of particles + startColor.r = 0.7f; + startColor.g = 0.8f; + startColor.b = 1.0f; + startColor.a = 1.0f; + startColorVar.r = 0.0f; + startColorVar.g = 0.0f; + startColorVar.b = 0.0f; + startColorVar.a = 0.0f; + endColor.r = 0.7f; + endColor.g = 0.8f; + endColor.b = 1.0f; + endColor.a = 0.5f; + endColorVar.r = 0.0f; + endColorVar.g = 0.0f; + endColorVar.b = 0.0f; + endColorVar.a = 0.0f; + + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; + + // additive + self.blendAdditive = NO; + } + + return self; +} +@end diff --git a/libs/cocos2d/CCParticleSystem.h b/libs/cocos2d/CCParticleSystem.h new file mode 100644 index 0000000..e405c43 --- /dev/null +++ b/libs/cocos2d/CCParticleSystem.h @@ -0,0 +1,444 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CCProtocols.h" +#import "CCNode.h" +#import "ccTypes.h" +#import "ccConfig.h" + +#if CC_ENABLE_PROFILERS +@class CCProfilingTimer; +#endif + +//* @enum +enum { + /** The Particle emitter lives forever */ + kCCParticleDurationInfinity = -1, + + /** The starting size of the particle is equal to the ending size */ + kCCParticleStartSizeEqualToEndSize = -1, + + /** The starting radius of the particle is equal to the ending radius */ + kCCParticleStartRadiusEqualToEndRadius = -1, + + // backward compatible + kParticleStartSizeEqualToEndSize = kCCParticleStartSizeEqualToEndSize, + kParticleDurationInfinity = kCCParticleDurationInfinity, +}; + +//* @enum +enum { + /** Gravity mode (A mode) */ + kCCParticleModeGravity, + + /** Radius mode (B mode) */ + kCCParticleModeRadius, +}; + + +/** @typedef tCCPositionType + possible types of particle positions + */ +typedef enum { + /** Living particles are attached to the world and are unaffected by emitter repositioning. */ + kCCPositionTypeFree, + + /** Living particles are attached to the world but will follow the emitter repositioning. + Use case: Attach an emitter to an sprite, and you want that the emitter follows the sprite. + */ + kCCPositionTypeRelative, + + /** Living particles are attached to the emitter and are translated along with it. */ + kCCPositionTypeGrouped, +}tCCPositionType; + +// backward compatible +enum { + kPositionTypeFree = kCCPositionTypeFree, + kPositionTypeGrouped = kCCPositionTypeGrouped, +}; + +/** @struct tCCParticle + Structure that contains the values of each particle + */ +typedef struct sCCParticle { + CGPoint pos; + CGPoint startPos; + + ccColor4F color; + ccColor4F deltaColor; + + float size; + float deltaSize; + + float rotation; + float deltaRotation; + + ccTime timeToLive; + + union { + // Mode A: gravity, direction, radial accel, tangential accel + struct { + CGPoint dir; + float radialAccel; + float tangentialAccel; + } A; + + // Mode B: radius mode + struct { + float angle; + float degreesPerSecond; + float radius; + float deltaRadius; + } B; + } mode; + +}tCCParticle; + +typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tCCParticle*, CGPoint); + +@class CCTexture2D; + +/** Particle System base class + Attributes of a Particle System: + - emmision rate of the particles + - Gravity Mode (Mode A): + - gravity + - direction + - speed +- variance + - tangential acceleration +- variance + - radial acceleration +- variance + - Radius Mode (Mode B): + - startRadius +- variance + - endRadius +- variance + - rotate +- variance + - Properties common to all modes: + - life +- life variance + - start spin +- variance + - end spin +- variance + - start size +- variance + - end size +- variance + - start color +- variance + - end color +- variance + - life +- variance + - blending function + - texture + + cocos2d also supports particles generated by Particle Designer (http://particledesigner.71squared.com/). + 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d, + cocos2d uses a another approach, but the results are almost identical. + + cocos2d supports all the variables used by Particle Designer plus a bit more: + - spinning particles (supported when using CCParticleSystemQuad) + - tangential acceleration (Gravity mode) + - radial acceleration (Gravity mode) + - radius direction (Radius mode) (Particle Designer supports outwards to inwards direction only) + + It is possible to customize any of the above mentioned properties in runtime. Example: + + @code + emitter.radialAccel = 15; + emitter.startSpin = 0; + @endcode + + */ +@interface CCParticleSystem : CCNode +{ + // is the particle system active ? + BOOL active; + // duration in seconds of the system. -1 is infinity + float duration; + // time elapsed since the start of the system (in seconds) + float elapsed; + + // position is from "superclass" CocosNode + CGPoint sourcePosition; + // Position variance + CGPoint posVar; + + // The angle (direction) of the particles measured in degrees + float angle; + // Angle variance measured in degrees; + float angleVar; + + // Different modes + + NSInteger emitterMode_; + union { + // Mode A:Gravity + Tangential Accel + Radial Accel + struct { + // gravity of the particles + CGPoint gravity; + + // The speed the particles will have. + float speed; + // The speed variance + float speedVar; + + // Tangential acceleration + float tangentialAccel; + // Tangential acceleration variance + float tangentialAccelVar; + + // Radial acceleration + float radialAccel; + // Radial acceleration variance + float radialAccelVar; + } A; + + // Mode B: circular movement (gravity, radial accel and tangential accel don't are not used in this mode) + struct { + + // The starting radius of the particles + float startRadius; + // The starting radius variance of the particles + float startRadiusVar; + // The ending radius of the particles + float endRadius; + // The ending radius variance of the particles + float endRadiusVar; + // Number of degress to rotate a particle around the source pos per second + float rotatePerSecond; + // Variance in degrees for rotatePerSecond + float rotatePerSecondVar; + } B; + } mode; + + // start ize of the particles + float startSize; + // start Size variance + float startSizeVar; + // End size of the particle + float endSize; + // end size of variance + float endSizeVar; + + // How many seconds will the particle live + float life; + // Life variance + float lifeVar; + + // Start color of the particles + ccColor4F startColor; + // Start color variance + ccColor4F startColorVar; + // End color of the particles + ccColor4F endColor; + // End color variance + ccColor4F endColorVar; + + // start angle of the particles + float startSpin; + // start angle variance + float startSpinVar; + // End angle of the particle + float endSpin; + // end angle ariance + float endSpinVar; + + + // Array of particles + tCCParticle *particles; + // Maximum particles + NSUInteger totalParticles; + // Count of active particles + NSUInteger particleCount; + + // color modulate +// BOOL colorModulate; + + // How many particles can be emitted per second + float emissionRate; + float emitCounter; + + // Texture of the particles + CCTexture2D *texture_; + // blend function + ccBlendFunc blendFunc_; + + // movment type: free or grouped + tCCPositionType positionType_; + + // Whether or not the node will be auto-removed when there are not particles + BOOL autoRemoveOnFinish_; + + // particle idx + NSUInteger particleIdx; + + // Optimization + CC_UPDATE_PARTICLE_IMP updateParticleImp; + SEL updateParticleSel; + +// profiling +#if CC_ENABLE_PROFILERS + CCProfilingTimer* _profilingTimer; +#endif +} + +/** Is the emitter active */ +@property (nonatomic,readonly) BOOL active; +/** Quantity of particles that are being simulated at the moment */ +@property (nonatomic,readonly) NSUInteger particleCount; +/** How many seconds the emitter wil run. -1 means 'forever' */ +@property (nonatomic,readwrite,assign) float duration; +/** sourcePosition of the emitter */ +@property (nonatomic,readwrite,assign) CGPoint sourcePosition; +/** Position variance of the emitter */ +@property (nonatomic,readwrite,assign) CGPoint posVar; +/** life, and life variation of each particle */ +@property (nonatomic,readwrite,assign) float life; +/** life variance of each particle */ +@property (nonatomic,readwrite,assign) float lifeVar; +/** angle and angle variation of each particle */ +@property (nonatomic,readwrite,assign) float angle; +/** angle variance of each particle */ +@property (nonatomic,readwrite,assign) float angleVar; + +/** Gravity value. Only available in 'Gravity' mode. */ +@property (nonatomic,readwrite,assign) CGPoint gravity; +/** speed of each particle. Only available in 'Gravity' mode. */ +@property (nonatomic,readwrite,assign) float speed; +/** speed variance of each particle. Only available in 'Gravity' mode. */ +@property (nonatomic,readwrite,assign) float speedVar; +/** tangential acceleration of each particle. Only available in 'Gravity' mode. */ +@property (nonatomic,readwrite,assign) float tangentialAccel; +/** tangential acceleration variance of each particle. Only available in 'Gravity' mode. */ +@property (nonatomic,readwrite,assign) float tangentialAccelVar; +/** radial acceleration of each particle. Only available in 'Gravity' mode. */ +@property (nonatomic,readwrite,assign) float radialAccel; +/** radial acceleration variance of each particle. Only available in 'Gravity' mode. */ +@property (nonatomic,readwrite,assign) float radialAccelVar; + +/** The starting radius of the particles. Only available in 'Radius' mode. */ +@property (nonatomic,readwrite,assign) float startRadius; +/** The starting radius variance of the particles. Only available in 'Radius' mode. */ +@property (nonatomic,readwrite,assign) float startRadiusVar; +/** The ending radius of the particles. Only available in 'Radius' mode. */ +@property (nonatomic,readwrite,assign) float endRadius; +/** The ending radius variance of the particles. Only available in 'Radius' mode. */ +@property (nonatomic,readwrite,assign) float endRadiusVar; +/** Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode. */ +@property (nonatomic,readwrite,assign) float rotatePerSecond; +/** Variance in degrees for rotatePerSecond. Only available in 'Radius' mode. */ +@property (nonatomic,readwrite,assign) float rotatePerSecondVar; + +/** start size in pixels of each particle */ +@property (nonatomic,readwrite,assign) float startSize; +/** size variance in pixels of each particle */ +@property (nonatomic,readwrite,assign) float startSizeVar; +/** end size in pixels of each particle */ +@property (nonatomic,readwrite,assign) float endSize; +/** end size variance in pixels of each particle */ +@property (nonatomic,readwrite,assign) float endSizeVar; +/** start color of each particle */ +@property (nonatomic,readwrite,assign) ccColor4F startColor; +/** start color variance of each particle */ +@property (nonatomic,readwrite,assign) ccColor4F startColorVar; +/** end color and end color variation of each particle */ +@property (nonatomic,readwrite,assign) ccColor4F endColor; +/** end color variance of each particle */ +@property (nonatomic,readwrite,assign) ccColor4F endColorVar; +//* initial angle of each particle +@property (nonatomic,readwrite,assign) float startSpin; +//* initial angle of each particle +@property (nonatomic,readwrite,assign) float startSpinVar; +//* initial angle of each particle +@property (nonatomic,readwrite,assign) float endSpin; +//* initial angle of each particle +@property (nonatomic,readwrite,assign) float endSpinVar; +/** emission rate of the particles */ +@property (nonatomic,readwrite,assign) float emissionRate; +/** maximum particles of the system */ +@property (nonatomic,readwrite,assign) NSUInteger totalParticles; +/** conforms to CocosNodeTexture protocol */ +@property (nonatomic,readwrite, retain) CCTexture2D * texture; +/** conforms to CocosNodeTexture protocol */ +@property (nonatomic,readwrite) ccBlendFunc blendFunc; +/** whether or not the particles are using blend additive. + If enabled, the following blending function will be used. + @code + source blend function = GL_SRC_ALPHA; + dest blend function = GL_ONE; + @endcode + */ +@property (nonatomic,readwrite) BOOL blendAdditive; +/** particles movement type: Free or Grouped + @since v0.8 + */ +@property (nonatomic,readwrite) tCCPositionType positionType; +/** whether or not the node will be auto-removed when it has no particles left. + By default it is NO. + @since v0.8 + */ +@property (nonatomic,readwrite) BOOL autoRemoveOnFinish; +/** Switch between different kind of emitter modes: + - kCCParticleModeGravity: uses gravity, speed, radial and tangential acceleration + - kCCParticleModeRadius: uses radius movement + rotation + */ +@property (nonatomic,readwrite) NSInteger emitterMode; + +/** creates an initializes a CCParticleSystem from a plist file. + This plist files can be creted manually or with Particle Designer: + http://particledesigner.71squared.com/ + @since v0.99.3 + */ ++(id) particleWithFile:(NSString*)plistFile; + +/** initializes a CCParticleSystem from a plist file. + This plist files can be creted manually or with Particle Designer: + http://particledesigner.71squared.com/ + @since v0.99.3 + */ +-(id) initWithFile:(NSString*) plistFile; + +/** initializes a CCQuadParticleSystem from a NSDictionary. + @since v0.99.3 + */ +-(id) initWithDictionary:(NSDictionary*)dictionary; + +//! Initializes a system with a fixed number of particles +-(id) initWithTotalParticles:(NSUInteger) numberOfParticles; +//! Add a particle to the emitter +-(BOOL) addParticle; +//! Initializes a particle +-(void) initParticle: (tCCParticle*) particle; +//! stop emitting particles. Running particles will continue to run until they die +-(void) stopSystem; +//! Kill all living particles. +-(void) resetSystem; +//! whether or not the system is full +-(BOOL) isFull; + +//! should be overriden by subclasses +-(void) updateQuadWithParticle:(tCCParticle*)particle newPosition:(CGPoint)pos; +//! should be overriden by subclasses +-(void) postStep; + +//! called in every loop. +-(void) update: (ccTime) dt; + +@end + diff --git a/libs/cocos2d/CCParticleSystem.m b/libs/cocos2d/CCParticleSystem.m new file mode 100644 index 0000000..0cc0d7a --- /dev/null +++ b/libs/cocos2d/CCParticleSystem.m @@ -0,0 +1,795 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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. + * + */ + + +// ideas taken from: +// . The ocean spray in your face [Jeff Lander] +// http://www.double.co.nz/dust/col0798.pdf +// . Building an Advanced Particle System [John van der Burg] +// http://www.gamasutra.com/features/20000623/vanderburg_01.htm +// . LOVE game engine +// http://love2d.org/ +// +// +// Radius mode support, from 71 squared +// http://particledesigner.71squared.com/ +// +// IMPORTANT: Particle Designer is supported by cocos2d, but +// 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d, +// cocos2d uses a another approach, but the results are almost identical. +// + +// opengl +#import "Platforms/CCGL.h" + +// cocos2d +#import "ccConfig.h" +#if CC_ENABLE_PROFILERS +#import "Support/CCProfiling.h" +#endif +#import "CCParticleSystem.h" +#import "CCTextureCache.h" +#import "ccMacros.h" + +// support +#import "Support/OpenGL_Internal.h" +#import "Support/CGPointExtension.h" +#import "Support/base64.h" +#import "Support/ZipUtils.h" +#import "Support/CCFileUtils.h" + +@implementation CCParticleSystem +@synthesize active, duration; +@synthesize sourcePosition, posVar; +@synthesize particleCount; +@synthesize life, lifeVar; +@synthesize angle, angleVar; +@synthesize startColor, startColorVar, endColor, endColorVar; +@synthesize startSpin, startSpinVar, endSpin, endSpinVar; +@synthesize emissionRate; +@synthesize totalParticles; +@synthesize startSize, startSizeVar; +@synthesize endSize, endSizeVar; +@synthesize blendFunc = blendFunc_; +@synthesize positionType = positionType_; +@synthesize autoRemoveOnFinish = autoRemoveOnFinish_; +@synthesize emitterMode = emitterMode_; + + ++(id) particleWithFile:(NSString*) plistFile +{ + return [[[self alloc] initWithFile:plistFile] autorelease]; +} + +-(id) init { + NSAssert(NO, @"CCParticleSystem: Init not supported."); + [self release]; + return nil; +} + +-(id) initWithFile:(NSString *)plistFile +{ + NSString *path = [CCFileUtils fullPathFromRelativePath:plistFile]; + NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; + + NSAssert( dict != nil, @"Particles: file not found"); + return [self initWithDictionary:dict]; +} + +-(id) initWithDictionary:(NSDictionary *)dictionary +{ + NSUInteger maxParticles = [[dictionary valueForKey:@"maxParticles"] intValue]; + // self, not super + if ((self=[self initWithTotalParticles:maxParticles] ) ) { + + // angle + angle = [[dictionary valueForKey:@"angle"] floatValue]; + angleVar = [[dictionary valueForKey:@"angleVariance"] floatValue]; + + // duration + duration = [[dictionary valueForKey:@"duration"] floatValue]; + + // blend function + blendFunc_.src = [[dictionary valueForKey:@"blendFuncSource"] intValue]; + blendFunc_.dst = [[dictionary valueForKey:@"blendFuncDestination"] intValue]; + + // color + float r,g,b,a; + + r = [[dictionary valueForKey:@"startColorRed"] floatValue]; + g = [[dictionary valueForKey:@"startColorGreen"] floatValue]; + b = [[dictionary valueForKey:@"startColorBlue"] floatValue]; + a = [[dictionary valueForKey:@"startColorAlpha"] floatValue]; + startColor = (ccColor4F) {r,g,b,a}; + + r = [[dictionary valueForKey:@"startColorVarianceRed"] floatValue]; + g = [[dictionary valueForKey:@"startColorVarianceGreen"] floatValue]; + b = [[dictionary valueForKey:@"startColorVarianceBlue"] floatValue]; + a = [[dictionary valueForKey:@"startColorVarianceAlpha"] floatValue]; + startColorVar = (ccColor4F) {r,g,b,a}; + + r = [[dictionary valueForKey:@"finishColorRed"] floatValue]; + g = [[dictionary valueForKey:@"finishColorGreen"] floatValue]; + b = [[dictionary valueForKey:@"finishColorBlue"] floatValue]; + a = [[dictionary valueForKey:@"finishColorAlpha"] floatValue]; + endColor = (ccColor4F) {r,g,b,a}; + + r = [[dictionary valueForKey:@"finishColorVarianceRed"] floatValue]; + g = [[dictionary valueForKey:@"finishColorVarianceGreen"] floatValue]; + b = [[dictionary valueForKey:@"finishColorVarianceBlue"] floatValue]; + a = [[dictionary valueForKey:@"finishColorVarianceAlpha"] floatValue]; + endColorVar = (ccColor4F) {r,g,b,a}; + + // particle size + startSize = [[dictionary valueForKey:@"startParticleSize"] floatValue]; + startSizeVar = [[dictionary valueForKey:@"startParticleSizeVariance"] floatValue]; + endSize = [[dictionary valueForKey:@"finishParticleSize"] floatValue]; + endSizeVar = [[dictionary valueForKey:@"finishParticleSizeVariance"] floatValue]; + + + // position + float x = [[dictionary valueForKey:@"sourcePositionx"] floatValue]; + float y = [[dictionary valueForKey:@"sourcePositiony"] floatValue]; + self.position = ccp(x,y); + posVar.x = [[dictionary valueForKey:@"sourcePositionVariancex"] floatValue]; + posVar.y = [[dictionary valueForKey:@"sourcePositionVariancey"] floatValue]; + + + emitterMode_ = [[dictionary valueForKey:@"emitterType"] intValue]; + + // Mode A: Gravity + tangential accel + radial accel + if( emitterMode_ == kCCParticleModeGravity ) { + // gravity + mode.A.gravity.x = [[dictionary valueForKey:@"gravityx"] floatValue]; + mode.A.gravity.y = [[dictionary valueForKey:@"gravityy"] floatValue]; + + // + // speed + mode.A.speed = [[dictionary valueForKey:@"speed"] floatValue]; + mode.A.speedVar = [[dictionary valueForKey:@"speedVariance"] floatValue]; + + // radial acceleration + NSString *tmp = [dictionary valueForKey:@"radialAcceleration"]; + mode.A.radialAccel = tmp ? [tmp floatValue] : 0; + + tmp = [dictionary valueForKey:@"radialAccelVariance"]; + mode.A.radialAccelVar = tmp ? [tmp floatValue] : 0; + + // tangential acceleration + tmp = [dictionary valueForKey:@"tangentialAcceleration"]; + mode.A.tangentialAccel = tmp ? [tmp floatValue] : 0; + + tmp = [dictionary valueForKey:@"tangentialAccelVariance"]; + mode.A.tangentialAccelVar = tmp ? [tmp floatValue] : 0; + } + + + // or Mode B: radius movement + else if( emitterMode_ == kCCParticleModeRadius ) { + float maxRadius = [[dictionary valueForKey:@"maxRadius"] floatValue]; + float maxRadiusVar = [[dictionary valueForKey:@"maxRadiusVariance"] floatValue]; + float minRadius = [[dictionary valueForKey:@"minRadius"] floatValue]; + + mode.B.startRadius = maxRadius; + mode.B.startRadiusVar = maxRadiusVar; + mode.B.endRadius = minRadius; + mode.B.endRadiusVar = 0; + mode.B.rotatePerSecond = [[dictionary valueForKey:@"rotatePerSecond"] floatValue]; + mode.B.rotatePerSecondVar = [[dictionary valueForKey:@"rotatePerSecondVariance"] floatValue]; + + } else { + NSAssert( NO, @"Invalid emitterType in config file"); + } + + // life span + life = [[dictionary valueForKey:@"particleLifespan"] floatValue]; + lifeVar = [[dictionary valueForKey:@"particleLifespanVariance"] floatValue]; + + // emission Rate + emissionRate = totalParticles/life; + + // texture + // Try to get the texture from the cache + NSString *textureName = [dictionary valueForKey:@"textureFileName"]; + + self.texture = [[CCTextureCache sharedTextureCache] addImage:textureName]; + + NSString *textureData = [dictionary valueForKey:@"textureImageData"]; + + if ( ! texture_ && textureData) { + + // if it fails, try to get it from the base64-gzipped data + unsigned char *buffer = NULL; + int len = base64Decode((unsigned char*)[textureData UTF8String], (unsigned int)[textureData length], &buffer); + NSAssert( buffer != NULL, @"CCParticleSystem: error decoding textureImageData"); + + unsigned char *deflated = NULL; + NSUInteger deflatedLen = ccInflateMemory(buffer, len, &deflated); + free( buffer ); + + NSAssert( deflated != NULL, @"CCParticleSystem: error ungzipping textureImageData"); + NSData *data = [[NSData alloc] initWithBytes:deflated length:deflatedLen]; + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + UIImage *image = [[UIImage alloc] initWithData:data]; +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + NSBitmapImageRep *image = [[NSBitmapImageRep alloc] initWithData:data]; +#endif + + free(deflated); deflated = NULL; + + self.texture = [[CCTextureCache sharedTextureCache] addCGImage:[image CGImage] forKey:textureName]; + [data release]; + [image release]; + } + + NSAssert( [self texture] != NULL, @"CCParticleSystem: error loading the texture"); + + } + + return self; +} + +-(id) initWithTotalParticles:(NSUInteger) numberOfParticles +{ + if( (self=[super init]) ) { + + totalParticles = numberOfParticles; + + particles = calloc( totalParticles, sizeof(tCCParticle) ); + + if( ! particles ) { + NSLog(@"Particle system: not enough memory"); + [self release]; + return nil; + } + + // default, active + active = YES; + + // default blend function + blendFunc_ = (ccBlendFunc) { CC_BLEND_SRC, CC_BLEND_DST }; + + // default movement type; + positionType_ = kCCPositionTypeFree; + + // by default be in mode A: + emitterMode_ = kCCParticleModeGravity; + + // default: modulate + // XXX: not used + // colorModulate = YES; + + autoRemoveOnFinish_ = NO; + + // profiling +#if CC_ENABLE_PROFILERS + _profilingTimer = [[CCProfiler timerWithName:@"particle system" andInstance:self] retain]; +#endif + + // Optimization: compile udpateParticle method + updateParticleSel = @selector(updateQuadWithParticle:newPosition:); + updateParticleImp = (CC_UPDATE_PARTICLE_IMP) [self methodForSelector:updateParticleSel]; + + // udpate after action in run! + [self scheduleUpdateWithPriority:1]; + + } + + return self; +} + +-(void) dealloc +{ + free( particles ); + + [texture_ release]; + // profiling +#if CC_ENABLE_PROFILERS + [CCProfiler releaseTimer:_profilingTimer]; +#endif + + [super dealloc]; +} + +-(BOOL) addParticle +{ + if( [self isFull] ) + return NO; + + tCCParticle * particle = &particles[ particleCount ]; + + [self initParticle: particle]; + particleCount++; + + return YES; +} + +-(void) initParticle: (tCCParticle*) particle +{ + + // timeToLive + // no negative life. prevent division by 0 + particle->timeToLive = MAX(0, life + lifeVar * CCRANDOM_MINUS1_1() ); + + // position + particle->pos.x = sourcePosition.x + posVar.x * CCRANDOM_MINUS1_1(); + particle->pos.x *= CC_CONTENT_SCALE_FACTOR(); + particle->pos.y = sourcePosition.y + posVar.y * CCRANDOM_MINUS1_1(); + particle->pos.y *= CC_CONTENT_SCALE_FACTOR(); + + // Color + ccColor4F start; + start.r = MIN(1, MAX(0, startColor.r + startColorVar.r * CCRANDOM_MINUS1_1() ) ); + start.g = MIN(1, MAX(0, startColor.g + startColorVar.g * CCRANDOM_MINUS1_1() ) ); + start.b = MIN(1, MAX(0, startColor.b + startColorVar.b * CCRANDOM_MINUS1_1() ) ); + start.a = MIN(1, MAX(0, startColor.a + startColorVar.a * CCRANDOM_MINUS1_1() ) ); + + ccColor4F end; + end.r = MIN(1, MAX(0, endColor.r + endColorVar.r * CCRANDOM_MINUS1_1() ) ); + end.g = MIN(1, MAX(0, endColor.g + endColorVar.g * CCRANDOM_MINUS1_1() ) ); + end.b = MIN(1, MAX(0, endColor.b + endColorVar.b * CCRANDOM_MINUS1_1() ) ); + end.a = MIN(1, MAX(0, endColor.a + endColorVar.a * CCRANDOM_MINUS1_1() ) ); + + particle->color = start; + particle->deltaColor.r = (end.r - start.r) / particle->timeToLive; + particle->deltaColor.g = (end.g - start.g) / particle->timeToLive; + particle->deltaColor.b = (end.b - start.b) / particle->timeToLive; + particle->deltaColor.a = (end.a - start.a) / particle->timeToLive; + + // size + float startS = MAX(0, startSize + startSizeVar * CCRANDOM_MINUS1_1() ); // no negative size + startS *= CC_CONTENT_SCALE_FACTOR(); + + particle->size = startS; + if( endSize == kCCParticleStartSizeEqualToEndSize ) + particle->deltaSize = 0; + else { + float endS = endSize + endSizeVar * CCRANDOM_MINUS1_1(); + endS = MAX(0, endS); + endS *= CC_CONTENT_SCALE_FACTOR(); + particle->deltaSize = (endS - startS) / particle->timeToLive; + } + + // rotation + float startA = startSpin + startSpinVar * CCRANDOM_MINUS1_1(); + float endA = endSpin + endSpinVar * CCRANDOM_MINUS1_1(); + particle->rotation = startA; + particle->deltaRotation = (endA - startA) / particle->timeToLive; + + // position + if( positionType_ == kCCPositionTypeFree ) { + CGPoint p = [self convertToWorldSpace:CGPointZero]; + particle->startPos = ccpMult( p, CC_CONTENT_SCALE_FACTOR() ); + } + else if( positionType_ == kCCPositionTypeRelative ) { + particle->startPos = ccpMult( position_, CC_CONTENT_SCALE_FACTOR() ); + } + + // direction + float a = CC_DEGREES_TO_RADIANS( angle + angleVar * CCRANDOM_MINUS1_1() ); + + // Mode Gravity: A + if( emitterMode_ == kCCParticleModeGravity ) { + + CGPoint v = {cosf( a ), sinf( a )}; + float s = mode.A.speed + mode.A.speedVar * CCRANDOM_MINUS1_1(); + s *= CC_CONTENT_SCALE_FACTOR(); + + // direction + particle->mode.A.dir = ccpMult( v, s ); + + // radial accel + particle->mode.A.radialAccel = mode.A.radialAccel + mode.A.radialAccelVar * CCRANDOM_MINUS1_1(); + particle->mode.A.radialAccel *= CC_CONTENT_SCALE_FACTOR(); + + // tangential accel + particle->mode.A.tangentialAccel = mode.A.tangentialAccel + mode.A.tangentialAccelVar * CCRANDOM_MINUS1_1(); + particle->mode.A.tangentialAccel *= CC_CONTENT_SCALE_FACTOR(); + + } + + // Mode Radius: B + else { + // Set the default diameter of the particle from the source position + float startRadius = mode.B.startRadius + mode.B.startRadiusVar * CCRANDOM_MINUS1_1(); + float endRadius = mode.B.endRadius + mode.B.endRadiusVar * CCRANDOM_MINUS1_1(); + + startRadius *= CC_CONTENT_SCALE_FACTOR(); + endRadius *= CC_CONTENT_SCALE_FACTOR(); + + particle->mode.B.radius = startRadius; + + if( mode.B.endRadius == kCCParticleStartRadiusEqualToEndRadius ) + particle->mode.B.deltaRadius = 0; + else + particle->mode.B.deltaRadius = (endRadius - startRadius) / particle->timeToLive; + + particle->mode.B.angle = a; + particle->mode.B.degreesPerSecond = CC_DEGREES_TO_RADIANS(mode.B.rotatePerSecond + mode.B.rotatePerSecondVar * CCRANDOM_MINUS1_1()); + } +} + +-(void) stopSystem +{ + active = NO; + elapsed = duration; + emitCounter = 0; +} + +-(void) resetSystem +{ + active = YES; + elapsed = 0; + for(particleIdx = 0; particleIdx < particleCount; ++particleIdx) { + tCCParticle *p = &particles[particleIdx]; + p->timeToLive = 0; + } +} + +-(BOOL) isFull +{ + return (particleCount == totalParticles); +} + +#pragma mark ParticleSystem - MainLoop +-(void) update: (ccTime) dt +{ + if( active && emissionRate ) { + float rate = 1.0f / emissionRate; + emitCounter += dt; + while( particleCount < totalParticles && emitCounter > rate ) { + [self addParticle]; + emitCounter -= rate; + } + + elapsed += dt; + if(duration != -1 && duration < elapsed) + [self stopSystem]; + } + + particleIdx = 0; + + +#if CC_ENABLE_PROFILERS + CCProfilingBeginTimingBlock(_profilingTimer); +#endif + + + CGPoint currentPosition = CGPointZero; + if( positionType_ == kCCPositionTypeFree ) { + currentPosition = [self convertToWorldSpace:CGPointZero]; + currentPosition.x *= CC_CONTENT_SCALE_FACTOR(); + currentPosition.y *= CC_CONTENT_SCALE_FACTOR(); + } + else if( positionType_ == kCCPositionTypeRelative ) { + currentPosition = position_; + currentPosition.x *= CC_CONTENT_SCALE_FACTOR(); + currentPosition.y *= CC_CONTENT_SCALE_FACTOR(); + } + + while( particleIdx < particleCount ) + { + tCCParticle *p = &particles[particleIdx]; + + // life + p->timeToLive -= dt; + + if( p->timeToLive > 0 ) { + + // Mode A: gravity, direction, tangential accel & radial accel + if( emitterMode_ == kCCParticleModeGravity ) { + CGPoint tmp, radial, tangential; + + radial = CGPointZero; + // radial acceleration + if(p->pos.x || p->pos.y) + radial = ccpNormalize(p->pos); + + tangential = radial; + radial = ccpMult(radial, p->mode.A.radialAccel); + + // tangential acceleration + float newy = tangential.x; + tangential.x = -tangential.y; + tangential.y = newy; + tangential = ccpMult(tangential, p->mode.A.tangentialAccel); + + // (gravity + radial + tangential) * dt + tmp = ccpAdd( ccpAdd( radial, tangential), mode.A.gravity); + tmp = ccpMult( tmp, dt); + p->mode.A.dir = ccpAdd( p->mode.A.dir, tmp); + tmp = ccpMult(p->mode.A.dir, dt); + p->pos = ccpAdd( p->pos, tmp ); + } + + // Mode B: radius movement + else { + // Update the angle and radius of the particle. + p->mode.B.angle += p->mode.B.degreesPerSecond * dt; + p->mode.B.radius += p->mode.B.deltaRadius * dt; + + p->pos.x = - cosf(p->mode.B.angle) * p->mode.B.radius; + p->pos.y = - sinf(p->mode.B.angle) * p->mode.B.radius; + } + + // color + p->color.r += (p->deltaColor.r * dt); + p->color.g += (p->deltaColor.g * dt); + p->color.b += (p->deltaColor.b * dt); + p->color.a += (p->deltaColor.a * dt); + + // size + p->size += (p->deltaSize * dt); + p->size = MAX( 0, p->size ); + + // angle + p->rotation += (p->deltaRotation * dt); + + // + // update values in quad + // + + CGPoint newPos; + + if( positionType_ == kCCPositionTypeFree || positionType_ == kCCPositionTypeRelative ) { + CGPoint diff = ccpSub( currentPosition, p->startPos ); + newPos = ccpSub(p->pos, diff); + + } else + newPos = p->pos; + + + updateParticleImp(self, updateParticleSel, p, newPos); + + // update particle counter + particleIdx++; + + } else { + // life < 0 + if( particleIdx != particleCount-1 ) + particles[particleIdx] = particles[particleCount-1]; + particleCount--; + + if( particleCount == 0 && autoRemoveOnFinish_ ) { + [self unscheduleUpdate]; + [parent_ removeChild:self cleanup:YES]; + return; + } + } + } + +#if CC_ENABLE_PROFILERS + CCProfilingEndTimingBlock(_profilingTimer); +#endif + +#ifdef CC_USES_VBO + [self postStep]; +#endif +} + +-(void) updateQuadWithParticle:(tCCParticle*)particle newPosition:(CGPoint)pos; +{ + // should be overriden +} + +-(void) postStep +{ + // should be overriden +} + +#pragma mark ParticleSystem - CCTexture protocol + +-(void) setTexture:(CCTexture2D*) texture +{ + [texture_ release]; + texture_ = [texture retain]; + + // If the new texture has No premultiplied alpha, AND the blendFunc hasn't been changed, then update it + if( texture_ && ! [texture hasPremultipliedAlpha] && + ( blendFunc_.src == CC_BLEND_SRC && blendFunc_.dst == CC_BLEND_DST ) ) { + + blendFunc_.src = GL_SRC_ALPHA; + blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; + } +} + +-(CCTexture2D*) texture +{ + return texture_; +} + +#pragma mark ParticleSystem - Additive Blending +-(void) setBlendAdditive:(BOOL)additive +{ + if( additive ) { + blendFunc_.src = GL_SRC_ALPHA; + blendFunc_.dst = GL_ONE; + + } else { + + if( texture_ && ! [texture_ hasPremultipliedAlpha] ) { + blendFunc_.src = GL_SRC_ALPHA; + blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; + } else { + blendFunc_.src = CC_BLEND_SRC; + blendFunc_.dst = CC_BLEND_DST; + } + } +} + +-(BOOL) blendAdditive +{ + return( blendFunc_.src == GL_SRC_ALPHA && blendFunc_.dst == GL_ONE); +} + +#pragma mark ParticleSystem - Properties of Gravity Mode +-(void) setTangentialAccel:(float)t +{ + NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); + mode.A.tangentialAccel = t; +} +-(float) tangentialAccel +{ + NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); + return mode.A.tangentialAccel; +} + +-(void) setTangentialAccelVar:(float)t +{ + NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); + mode.A.tangentialAccelVar = t; +} +-(float) tangentialAccelVar +{ + NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); + return mode.A.tangentialAccelVar; +} + +-(void) setRadialAccel:(float)t +{ + NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); + mode.A.radialAccel = t; +} +-(float) radialAccel +{ + NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); + return mode.A.radialAccel; +} + +-(void) setRadialAccelVar:(float)t +{ + NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); + mode.A.radialAccelVar = t; +} +-(float) radialAccelVar +{ + NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); + return mode.A.radialAccelVar; +} + +-(void) setGravity:(CGPoint)g +{ + NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); + mode.A.gravity = g; +} +-(CGPoint) gravity +{ + NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); + return mode.A.gravity; +} + +-(void) setSpeed:(float)speed +{ + NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); + mode.A.speed = speed; +} +-(float) speed +{ + NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); + return mode.A.speed; +} + +-(void) setSpeedVar:(float)speedVar +{ + NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); + mode.A.speedVar = speedVar; +} +-(float) speedVar +{ + NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); + return mode.A.speedVar; +} + +#pragma mark ParticleSystem - Properties of Radius Mode + +-(void) setStartRadius:(float)startRadius +{ + NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); + mode.B.startRadius = startRadius; +} +-(float) startRadius +{ + NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); + return mode.B.startRadius; +} + +-(void) setStartRadiusVar:(float)startRadiusVar +{ + NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); + mode.B.startRadiusVar = startRadiusVar; +} +-(float) startRadiusVar +{ + NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); + return mode.B.startRadiusVar; +} + +-(void) setEndRadius:(float)endRadius +{ + NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); + mode.B.endRadius = endRadius; +} +-(float) endRadius +{ + NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); + return mode.B.endRadius; +} + +-(void) setEndRadiusVar:(float)endRadiusVar +{ + NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); + mode.B.endRadiusVar = endRadiusVar; +} +-(float) endRadiusVar +{ + NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); + return mode.B.endRadiusVar; +} + +-(void) setRotatePerSecond:(float)degrees +{ + NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); + mode.B.rotatePerSecond = degrees; +} +-(float) rotatePerSecond +{ + NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); + return mode.B.rotatePerSecond; +} + +-(void) setRotatePerSecondVar:(float)degrees +{ + NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); + mode.B.rotatePerSecondVar = degrees; +} +-(float) rotatePerSecondVar +{ + NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); + return mode.B.rotatePerSecondVar; +} +@end + + diff --git a/libs/cocos2d/CCParticleSystemPoint.h b/libs/cocos2d/CCParticleSystemPoint.h new file mode 100644 index 0000000..029ad41 --- /dev/null +++ b/libs/cocos2d/CCParticleSystemPoint.h @@ -0,0 +1,64 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 +#import "CCParticleSystem.h" + +#define CC_MAX_PARTICLE_SIZE 64 + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +/** CCParticleSystemPoint is a subclass of CCParticleSystem + Attributes of a Particle System: + * All the attributes of Particle System + + Features: + * consumes small memory: uses 1 vertex (x,y) per particle, no need to assign tex coordinates + * size can't be bigger than 64 + * the system can't be scaled since the particles are rendered using GL_POINT_SPRITE + + Limitations: + * On 3rd gen iPhone devices and iPads, this node performs MUCH slower than CCParticleSystemQuad. + */ +@interface CCParticleSystemPoint : CCParticleSystem +{ + // Array of (x,y,size) + ccPointSprite *vertices; + // vertices buffer id +#if CC_USES_VBO + GLuint verticesID; +#endif +} +@end + +#elif __MAC_OS_X_VERSION_MAX_ALLOWED + +#import "CCParticleSystemQuad.h" + +@interface CCParticleSystemPoint : CCParticleSystemQuad +@end + +#endif diff --git a/libs/cocos2d/CCParticleSystemPoint.m b/libs/cocos2d/CCParticleSystemPoint.m new file mode 100644 index 0000000..6b47218 --- /dev/null +++ b/libs/cocos2d/CCParticleSystemPoint.m @@ -0,0 +1,208 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 +#import "CCParticleSystemPoint.h" + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +// opengl +#import "Platforms/CCGL.h" + +// cocos2d +#import "CCTextureCache.h" +#import "ccMacros.h" + +// support +#import "Support/OpenGL_Internal.h" +#import "Support/CGPointExtension.h" + +@implementation CCParticleSystemPoint + +-(id) initWithTotalParticles:(NSUInteger) numberOfParticles +{ + if( (self=[super initWithTotalParticles:numberOfParticles]) ) { + + vertices = malloc( sizeof(ccPointSprite) * totalParticles ); + + if( ! vertices ) { + NSLog(@"cocos2d: Particle system: not enough memory"); + [self release]; + return nil; + } + +#if CC_USES_VBO + glGenBuffers(1, &verticesID); + + // initial binding + glBindBuffer(GL_ARRAY_BUFFER, verticesID); + glBufferData(GL_ARRAY_BUFFER, sizeof(ccPointSprite)*totalParticles, vertices, GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); +#endif + } + + return self; +} + +-(void) dealloc +{ + free(vertices); +#if CC_USES_VBO + glDeleteBuffers(1, &verticesID); +#endif + + [super dealloc]; +} + +-(void) updateQuadWithParticle:(tCCParticle*)p newPosition:(CGPoint)newPos +{ + // place vertices and colos in array + vertices[particleIdx].pos = (ccVertex2F) {newPos.x, newPos.y}; + vertices[particleIdx].size = p->size; + ccColor4B color = { p->color.r*255, p->color.g*255, p->color.b*255, p->color.a*255 }; + vertices[particleIdx].color = color; +} + +-(void) postStep +{ +#if CC_USES_VBO + glBindBuffer(GL_ARRAY_BUFFER, verticesID); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(ccPointSprite)*particleCount, vertices); + glBindBuffer(GL_ARRAY_BUFFER, 0); +#endif +} + +-(void) draw +{ + if (particleIdx==0) + return; + + // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY + // Unneeded states: GL_TEXTURE_COORD_ARRAY + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glBindTexture(GL_TEXTURE_2D, texture_.name); + + glEnable(GL_POINT_SPRITE_OES); + glTexEnvi( GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE ); + +#define kPointSize sizeof(vertices[0]) + +#if CC_USES_VBO + glBindBuffer(GL_ARRAY_BUFFER, verticesID); + + glVertexPointer(2,GL_FLOAT, kPointSize, 0); + + glColorPointer(4, GL_UNSIGNED_BYTE, kPointSize, (GLvoid*) offsetof(ccPointSprite, color) ); + + glEnableClientState(GL_POINT_SIZE_ARRAY_OES); + glPointSizePointerOES(GL_FLOAT, kPointSize, (GLvoid*) offsetof(ccPointSprite, size) ); +#else // Uses Vertex Array List + int offset = (int)vertices; + glVertexPointer(2,GL_FLOAT, kPointSize, (GLvoid*) offset); + + int diff = offsetof(ccPointSprite, color); + glColorPointer(4, GL_UNSIGNED_BYTE, kPointSize, (GLvoid*) (offset+diff)); + + glEnableClientState(GL_POINT_SIZE_ARRAY_OES); + diff = offsetof(ccPointSprite, size); + glPointSizePointerOES(GL_FLOAT, kPointSize, (GLvoid*) (offset+diff)); +#endif + + BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST; + if( newBlend ) + glBlendFunc( blendFunc_.src, blendFunc_.dst ); + + + glDrawArrays(GL_POINTS, 0, particleIdx); + + // restore blend state + if( newBlend ) + glBlendFunc( CC_BLEND_SRC, CC_BLEND_DST); + + +#if CC_USES_VBO + // unbind VBO buffer + glBindBuffer(GL_ARRAY_BUFFER, 0); +#endif + + glDisableClientState(GL_POINT_SIZE_ARRAY_OES); + glDisable(GL_POINT_SPRITE_OES); + + // restore GL default state + glEnableClientState(GL_TEXTURE_COORD_ARRAY); +} + +#pragma mark Non supported properties + +// +// SPIN IS NOT SUPPORTED +// +-(void) setStartSpin:(float)a +{ + NSAssert(a == 0, @"PointParticleSystem doesn't support spinning"); + [super setStartSpin:a]; +} +-(void) setStartSpinVar:(float)a +{ + NSAssert(a == 0, @"PointParticleSystem doesn't support spinning"); + [super setStartSpin:a]; +} +-(void) setEndSpin:(float)a +{ + NSAssert(a == 0, @"PointParticleSystem doesn't support spinning"); + [super setStartSpin:a]; +} +-(void) setEndSpinVar:(float)a +{ + NSAssert(a == 0, @"PointParticleSystem doesn't support spinning"); + [super setStartSpin:a]; +} + +// +// SIZE > 64 IS NOT SUPPORTED +// +-(void) setStartSize:(float)size +{ + NSAssert(size >= 0 && size <= CC_MAX_PARTICLE_SIZE, @"PointParticleSystem only supports 0 <= size <= 64"); + [super setStartSize:size]; +} + +-(void) setEndSize:(float)size +{ + NSAssert( (size == kCCParticleStartSizeEqualToEndSize) || + ( size >= 0 && size <= CC_MAX_PARTICLE_SIZE), @"PointParticleSystem only supports 0 <= size <= 64"); + [super setEndSize:size]; +} +@end + +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +@implementation CCParticleSystemPoint +@end + +#endif // __MAC_OS_X_VERSION_MAX_ALLOWED + + diff --git a/libs/cocos2d/CCParticleSystemQuad.h b/libs/cocos2d/CCParticleSystemQuad.h new file mode 100644 index 0000000..6f86ca7 --- /dev/null +++ b/libs/cocos2d/CCParticleSystemQuad.h @@ -0,0 +1,74 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2009 Leonardo Kasperavičius + * + * 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 "CCParticleSystem.h" +#import "ccConfig.h" + +@class CCSpriteFrame; + +/** CCParticleSystemQuad is a subclass of CCParticleSystem + + It includes all the features of ParticleSystem. + + Special features and Limitations: + - Particle size can be any float number. + - The system can be scaled + - The particles can be rotated + - On 1st and 2nd gen iPhones: It is only a bit slower that CCParticleSystemPoint + - On 3rd gen iPhone and iPads: It is MUCH faster than CCParticleSystemPoint + - It consumes more RAM and more GPU memory than CCParticleSystemPoint + - It supports subrects + @since v0.8 + */ +@interface CCParticleSystemQuad : CCParticleSystem +{ + ccV2F_C4B_T2F_Quad *quads_; // quads to be rendered + GLushort *indices_; // indices +#if CC_USES_VBO + GLuint quadsID_; // VBO id +#endif +} + +/** initialices the indices for the vertices */ +-(void) initIndices; + +/** initilizes the texture with a rectangle measured Points */ +-(void) initTexCoordsWithRect:(CGRect)rect; + +/** Sets a new CCSpriteFrame as particle. + WARNING: this method is experimental. Use setTexture:withRect instead. + @since v0.99.4 + */ +-(void)setDisplayFrame:(CCSpriteFrame*)spriteFrame; + +/** Sets a new texture with a rect. The rect is in Points. + @since v0.99.4 + */ +-(void) setTexture:(CCTexture2D *)texture withRect:(CGRect)rect; + +@end + diff --git a/libs/cocos2d/CCParticleSystemQuad.m b/libs/cocos2d/CCParticleSystemQuad.m new file mode 100644 index 0000000..afd4547 --- /dev/null +++ b/libs/cocos2d/CCParticleSystemQuad.m @@ -0,0 +1,316 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2009 Leonardo Kasperavičius + * + * 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. + * + */ + + +// opengl +#import "Platforms/CCGL.h" + +// cocos2d +#import "ccConfig.h" +#import "CCParticleSystemQuad.h" +#import "CCTextureCache.h" +#import "ccMacros.h" +#import "CCSpriteFrame.h" + +// support +#import "Support/OpenGL_Internal.h" +#import "Support/CGPointExtension.h" + +@implementation CCParticleSystemQuad + + +// overriding the init method +-(id) initWithTotalParticles:(NSUInteger) numberOfParticles +{ + // base initialization + if( (self=[super initWithTotalParticles:numberOfParticles]) ) { + + // allocating data space + quads_ = calloc( sizeof(quads_[0]) * totalParticles, 1 ); + indices_ = calloc( sizeof(indices_[0]) * totalParticles * 6, 1 ); + + if( !quads_ || !indices_) { + NSLog(@"cocos2d: Particle system: not enough memory"); + if( quads_ ) + free( quads_ ); + if(indices_) + free(indices_); + + [self release]; + return nil; + } + + // initialize only once the texCoords and the indices + [self initTexCoordsWithRect:CGRectMake(0, 0, [texture_ pixelsWide], [texture_ pixelsHigh])]; + [self initIndices]; + +#if CC_USES_VBO + // create the VBO buffer + glGenBuffers(1, &quadsID_); + + // initial binding + glBindBuffer(GL_ARRAY_BUFFER, quadsID_); + glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0])*totalParticles, quads_,GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); +#endif + } + + return self; +} + +-(void) dealloc +{ + free(quads_); + free(indices_); +#if CC_USES_VBO + glDeleteBuffers(1, &quadsID_); +#endif + + [super dealloc]; +} + +// rect is in Points coordinates. +-(void) initTexCoordsWithRect:(CGRect)pointRect +{ + // convert to Tex coords + + CGRect rect = CGRectMake( + pointRect.origin.x * CC_CONTENT_SCALE_FACTOR(), + pointRect.origin.y * CC_CONTENT_SCALE_FACTOR(), + pointRect.size.width * CC_CONTENT_SCALE_FACTOR(), + pointRect.size.height * CC_CONTENT_SCALE_FACTOR() ); + + GLfloat wide = [texture_ pixelsWide]; + GLfloat high = [texture_ pixelsHigh]; + +#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL + GLfloat left = (rect.origin.x*2+1) / (wide*2); + GLfloat bottom = (rect.origin.y*2+1) / (high*2); + GLfloat right = left + (rect.size.width*2-2) / (wide*2); + GLfloat top = bottom + (rect.size.height*2-2) / (high*2); +#else + GLfloat left = rect.origin.x / wide; + GLfloat bottom = rect.origin.y / high; + GLfloat right = left + rect.size.width / wide; + GLfloat top = bottom + rect.size.height / high; +#endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL + + // Important. Texture in cocos2d are inverted, so the Y component should be inverted + CC_SWAP( top, bottom); + + for(NSUInteger i=0; icolor.r*255, p->color.g*255, p->color.b*255, p->color.a*255}; + quad->bl.colors = color; + quad->br.colors = color; + quad->tl.colors = color; + quad->tr.colors = color; + + // vertices + GLfloat size_2 = p->size/2; + if( p->rotation ) { + GLfloat x1 = -size_2; + GLfloat y1 = -size_2; + + GLfloat x2 = size_2; + GLfloat y2 = size_2; + GLfloat x = newPos.x; + GLfloat y = newPos.y; + + GLfloat r = (GLfloat)-CC_DEGREES_TO_RADIANS(p->rotation); + GLfloat cr = cosf(r); + GLfloat sr = sinf(r); + GLfloat ax = x1 * cr - y1 * sr + x; + GLfloat ay = x1 * sr + y1 * cr + y; + GLfloat bx = x2 * cr - y1 * sr + x; + GLfloat by = x2 * sr + y1 * cr + y; + GLfloat cx = x2 * cr - y2 * sr + x; + GLfloat cy = x2 * sr + y2 * cr + y; + GLfloat dx = x1 * cr - y2 * sr + x; + GLfloat dy = x1 * sr + y2 * cr + y; + + // bottom-left + quad->bl.vertices.x = ax; + quad->bl.vertices.y = ay; + + // bottom-right vertex: + quad->br.vertices.x = bx; + quad->br.vertices.y = by; + + // top-left vertex: + quad->tl.vertices.x = dx; + quad->tl.vertices.y = dy; + + // top-right vertex: + quad->tr.vertices.x = cx; + quad->tr.vertices.y = cy; + } else { + // bottom-left vertex: + quad->bl.vertices.x = newPos.x - size_2; + quad->bl.vertices.y = newPos.y - size_2; + + // bottom-right vertex: + quad->br.vertices.x = newPos.x + size_2; + quad->br.vertices.y = newPos.y - size_2; + + // top-left vertex: + quad->tl.vertices.x = newPos.x - size_2; + quad->tl.vertices.y = newPos.y + size_2; + + // top-right vertex: + quad->tr.vertices.x = newPos.x + size_2; + quad->tr.vertices.y = newPos.y + size_2; + } +} + +-(void) postStep +{ +#if CC_USES_VBO + glBindBuffer(GL_ARRAY_BUFFER, quadsID_); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(quads_[0])*particleCount, quads_); + glBindBuffer(GL_ARRAY_BUFFER, 0); +#endif +} + +// overriding draw method +-(void) draw +{ + // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Unneeded states: - + + glBindTexture(GL_TEXTURE_2D, [texture_ name]); + +#define kQuadSize sizeof(quads_[0].bl) + +#if CC_USES_VBO + glBindBuffer(GL_ARRAY_BUFFER, quadsID_); + + glVertexPointer(2,GL_FLOAT, kQuadSize, 0); + + glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*) offsetof(ccV2F_C4B_T2F,colors) ); + + glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*) offsetof(ccV2F_C4B_T2F,texCoords) ); +#else // vertex array list + + NSUInteger offset = (NSUInteger) quads_; + + // vertex + NSUInteger diff = offsetof( ccV2F_C4B_T2F, vertices); + glVertexPointer(2,GL_FLOAT, kQuadSize, (GLvoid*) (offset+diff) ); + + // color + diff = offsetof( ccV2F_C4B_T2F, colors); + glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*)(offset + diff)); + + // tex coords + diff = offsetof( ccV2F_C4B_T2F, texCoords); + glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*)(offset + diff)); + +#endif // ! CC_USES_VBO + + BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST; + if( newBlend ) + glBlendFunc( blendFunc_.src, blendFunc_.dst ); + + NSAssert( particleIdx == particleCount, @"Abnormal error in particle quad"); + glDrawElements(GL_TRIANGLES, (GLsizei) particleIdx*6, GL_UNSIGNED_SHORT, indices_); + + // restore blend state + if( newBlend ) + glBlendFunc( CC_BLEND_SRC, CC_BLEND_DST ); + +#if CC_USES_VBO + glBindBuffer(GL_ARRAY_BUFFER, 0); +#endif + + // restore GL default state + // - +} + +@end + + diff --git a/libs/cocos2d/CCProgressTimer.h b/libs/cocos2d/CCProgressTimer.h new file mode 100644 index 0000000..9a07f2f --- /dev/null +++ b/libs/cocos2d/CCProgressTimer.h @@ -0,0 +1,83 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Lam Pham + * + * 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 +#import "CCSprite.h" + +/** Types of progress + @since v0.99.1 + */ +typedef enum { + /// Radial Counter-Clockwise + kCCProgressTimerTypeRadialCCW, + /// Radial ClockWise + kCCProgressTimerTypeRadialCW, + /// Horizontal Left-Right + kCCProgressTimerTypeHorizontalBarLR, + /// Horizontal Right-Left + kCCProgressTimerTypeHorizontalBarRL, + /// Vertical Bottom-top + kCCProgressTimerTypeVerticalBarBT, + /// Vertical Top-Bottom + kCCProgressTimerTypeVerticalBarTB, +} CCProgressTimerType; + +/** + CCProgresstimer is a subclass of CCNode. + It renders the inner sprite according to the percentage. + The progress can be Radial, Horizontal or vertical. + @since v0.99.1 + */ +@interface CCProgressTimer : CCNode +{ + CCProgressTimerType type_; + float percentage_; + CCSprite *sprite_; + + int vertexDataCount_; + ccV2F_C4B_T2F *vertexData_; +} + +/** Change the percentage to change progress. */ +@property (nonatomic, readwrite) CCProgressTimerType type; + +/** Percentages are from 0 to 100 */ +@property (nonatomic, readwrite) float percentage; + +/** The image to show the progress percentage */ +@property (nonatomic, readwrite, retain) CCSprite *sprite; + + +/** Creates a progress timer with an image filename as the shape the timer goes through */ ++ (id) progressWithFile:(NSString*) filename; +/** Initializes a progress timer with an image filename as the shape the timer goes through */ +- (id) initWithFile:(NSString*) filename; + +/** Creates a progress timer with the texture as the shape the timer goes through */ ++ (id) progressWithTexture:(CCTexture2D*) texture; +/** Creates a progress timer with the texture as the shape the timer goes through */ +- (id) initWithTexture:(CCTexture2D*) texture; + +@end diff --git a/libs/cocos2d/CCProgressTimer.m b/libs/cocos2d/CCProgressTimer.m new file mode 100644 index 0000000..ca11a80 --- /dev/null +++ b/libs/cocos2d/CCProgressTimer.m @@ -0,0 +1,491 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Lam Pham + * + * 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 "CCProgressTimer.h" + +#import "ccMacros.h" +#import "CCTextureCache.h" +#import "Support/CGPointExtension.h" + + + +#define kProgressTextureCoordsCount 4 +// kProgressTextureCoords holds points {0,0} {0,1} {1,1} {1,0} we can represent it as bits +const char kProgressTextureCoords = 0x1e; + +@interface CCProgressTimer (Internal) + +-(void)updateProgress; +-(void)updateBar; +-(void)updateRadial; +-(void)updateColor; +-(CGPoint)boundaryTexCoord:(char)index; +@end + + +@implementation CCProgressTimer +@synthesize percentage = percentage_; +@synthesize sprite = sprite_; +@synthesize type = type_; + ++(id)progressWithFile:(NSString*) filename +{ + return [[[self alloc]initWithFile:filename] autorelease]; +} +-(id)initWithFile:(NSString*) filename +{ + return [self initWithTexture:[[CCTextureCache sharedTextureCache] addImage: filename]]; +} + ++(id)progressWithTexture:(CCTexture2D*) texture +{ + return [[[self alloc]initWithTexture:texture] autorelease]; +} +-(id)initWithTexture:(CCTexture2D*) texture +{ + if(( self = [super init] )){ + self.sprite = [CCSprite spriteWithTexture:texture]; + percentage_ = 0.f; + vertexData_ = NULL; + vertexDataCount_ = 0; + self.anchorPoint = ccp(.5f,.5f); + self.contentSize = sprite_.contentSize; + self.type = kCCProgressTimerTypeRadialCCW; + } + return self; +} +-(void)dealloc +{ + if(vertexData_) + free(vertexData_); + + [sprite_ release]; + [super dealloc]; +} + +-(void)setPercentage:(float) percentage +{ + if(percentage_ != percentage) { + percentage_ = clampf( percentage, 0, 100); + [self updateProgress]; + } +} +-(void)setSprite:(CCSprite *)newSprite +{ + if(sprite_ != newSprite){ + [sprite_ release]; + sprite_ = [newSprite retain]; + + // Everytime we set a new sprite, we free the current vertex data + if(vertexData_){ + free(vertexData_); + vertexData_ = NULL; + vertexDataCount_ = 0; + } + } +} +-(void)setType:(CCProgressTimerType)newType +{ + if (newType != type_) { + + // release all previous information + if(vertexData_){ + free(vertexData_); + vertexData_ = NULL; + vertexDataCount_ = 0; + } + type_ = newType; + } +} +@end + +@implementation CCProgressTimer(Internal) + +/// +// @returns the vertex position from the texture coordinate +/// +-(ccVertex2F)vertexFromTexCoord:(CGPoint) texCoord +{ + CGPoint tmp; + ccVertex2F ret; + if (sprite_.texture) { + CCTexture2D *texture = [sprite_ texture]; + CGSize texSize = [texture contentSizeInPixels]; + tmp = ccp(texSize.width * texCoord.x/texture.maxS, + texSize.height * (1 - (texCoord.y/texture.maxT))); + } else + tmp = CGPointZero; + + ret.x = tmp.x; + ret.y = tmp.y; + return ret; +} + +-(void)updateColor +{ + GLubyte op = sprite_.opacity; + ccColor3B c3b = sprite_.color; + + ccColor4B color = { c3b.r, c3b.g, c3b.b, op }; + if([sprite_.texture hasPremultipliedAlpha]){ + color.r *= op/255; + color.g *= op/255; + color.b *= op/255; + } + + if(vertexData_){ + for (int i=0; i < vertexDataCount_; ++i) { + vertexData_[i].colors = color; + } + } +} + +-(void)updateProgress +{ + switch (type_) { + case kCCProgressTimerTypeRadialCW: + case kCCProgressTimerTypeRadialCCW: + [self updateRadial]; + break; + case kCCProgressTimerTypeHorizontalBarLR: + case kCCProgressTimerTypeHorizontalBarRL: + case kCCProgressTimerTypeVerticalBarBT: + case kCCProgressTimerTypeVerticalBarTB: + [self updateBar]; + break; + default: + break; + } +} + +/// +// Update does the work of mapping the texture onto the triangles +// It now doesn't occur the cost of free/alloc data every update cycle. +// It also only changes the percentage point but no other points if they have not +// been modified. +// +// It now deals with flipped texture. If you run into this problem, just use the +// sprite property and enable the methods flipX, flipY. +/// +-(void)updateRadial +{ + // Texture Max is the actual max coordinates to deal with non-power of 2 textures + CGPoint tMax = ccp(sprite_.texture.maxS,sprite_.texture.maxT); + + // Grab the midpoint + CGPoint midpoint = ccpCompMult(self.anchorPoint, tMax); + + float alpha = percentage_ / 100.f; + + // Otherwise we can get the angle from the alpha + float angle = 2.f*((float)M_PI) * ( type_ == kCCProgressTimerTypeRadialCW? alpha : 1.f - alpha); + + // We find the vector to do a hit detection based on the percentage + // We know the first vector is the one @ 12 o'clock (top,mid) so we rotate + // from that by the progress angle around the midpoint pivot + CGPoint topMid = ccp(midpoint.x, 0.f); + CGPoint percentagePt = ccpRotateByAngle(topMid, midpoint, angle); + + + int index = 0; + CGPoint hit = CGPointZero; + + if (alpha == 0.f) { + // More efficient since we don't always need to check intersection + // If the alpha is zero then the hit point is top mid and the index is 0. + hit = topMid; + index = 0; + } else if (alpha == 1.f) { + // More efficient since we don't always need to check intersection + // If the alpha is one then the hit point is top mid and the index is 4. + hit = topMid; + index = 4; + } else { + // We run a for loop checking the edges of the texture to find the + // intersection point + // We loop through five points since the top is split in half + + float min_t = FLT_MAX; + + for (int i = 0; i <= kProgressTextureCoordsCount; ++i) { + int pIndex = (i + (kProgressTextureCoordsCount - 1))%kProgressTextureCoordsCount; + + CGPoint edgePtA = ccpCompMult([self boundaryTexCoord:i % kProgressTextureCoordsCount],tMax); + CGPoint edgePtB = ccpCompMult([self boundaryTexCoord:pIndex],tMax); + + // Remember that the top edge is split in half for the 12 o'clock position + // Let's deal with that here by finding the correct endpoints + if(i == 0){ + edgePtB = ccpLerp(edgePtA,edgePtB,.5f); + } else if(i == 4){ + edgePtA = ccpLerp(edgePtA,edgePtB,.5f); + } + + // s and t are returned by ccpLineIntersect + float s = 0, t = 0; + if(ccpLineIntersect(edgePtA, edgePtB, midpoint, percentagePt, &s, &t)) + { + + // Since our hit test is on rays we have to deal with the top edge + // being in split in half so we have to test as a segment + if ((i == 0 || i == 4)) { + // s represents the point between edgePtA--edgePtB + if (!(0.f <= s && s <= 1.f)) { + continue; + } + } + // As long as our t isn't negative we are at least finding a + // correct hitpoint from midpoint to percentagePt. + if (t >= 0.f) { + // Because the percentage line and all the texture edges are + // rays we should only account for the shortest intersection + if (t < min_t) { + min_t = t; + index = i; + } + } + } + } + + // Now that we have the minimum magnitude we can use that to find our intersection + hit = ccpAdd(midpoint, ccpMult(ccpSub(percentagePt, midpoint),min_t)); + + } + + + // The size of the vertex data is the index from the hitpoint + // the 3 is for the midpoint, 12 o'clock point and hitpoint position. + + BOOL sameIndexCount = YES; + if(vertexDataCount_ != index + 3){ + sameIndexCount = NO; + if(vertexData_){ + free(vertexData_); + vertexData_ = NULL; + vertexDataCount_ = 0; + } + } + + + if(!vertexData_) { + vertexDataCount_ = index + 3; + vertexData_ = malloc(vertexDataCount_ * sizeof(ccV2F_C4B_T2F)); + NSAssert( vertexData_, @"CCProgressTimer. Not enough memory"); + + [self updateColor]; + } + + if (!sameIndexCount) { + + // First we populate the array with the midpoint, then all + // vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint + vertexData_[0].texCoords = (ccTex2F){midpoint.x, midpoint.y}; + vertexData_[0].vertices = [self vertexFromTexCoord:midpoint]; + + vertexData_[1].texCoords = (ccTex2F){midpoint.x, 0.f}; + vertexData_[1].vertices = [self vertexFromTexCoord:ccp(midpoint.x, 0.f)]; + + for(int i = 0; i < index; ++i){ + CGPoint texCoords = ccpCompMult([self boundaryTexCoord:i], tMax); + + vertexData_[i+2].texCoords = (ccTex2F){texCoords.x, texCoords.y}; + vertexData_[i+2].vertices = [self vertexFromTexCoord:texCoords]; + } + + // Flip the texture coordinates if set + if (sprite_.flipY || sprite_.flipX) { + for(int i = 0; i < vertexDataCount_ - 1; ++i){ + if (sprite_.flipX) { + vertexData_[i].texCoords.u = tMax.x - vertexData_[i].texCoords.u; + } + if(sprite_.flipY){ + vertexData_[i].texCoords.v = tMax.y - vertexData_[i].texCoords.v; + } + } + } + } + + // hitpoint will go last + vertexData_[vertexDataCount_ - 1].texCoords = (ccTex2F){hit.x, hit.y}; + vertexData_[vertexDataCount_ - 1].vertices = [self vertexFromTexCoord:hit]; + + if (sprite_.flipY || sprite_.flipX) { + if (sprite_.flipX) { + vertexData_[vertexDataCount_ - 1].texCoords.u = tMax.x - vertexData_[vertexDataCount_ - 1].texCoords.u; + } + if(sprite_.flipY){ + vertexData_[vertexDataCount_ - 1].texCoords.v = tMax.y - vertexData_[vertexDataCount_ - 1].texCoords.v; + } + } +} + +/// +// Update does the work of mapping the texture onto the triangles for the bar +// It now doesn't occur the cost of free/alloc data every update cycle. +// It also only changes the percentage point but no other points if they have not +// been modified. +// +// It now deals with flipped texture. If you run into this problem, just use the +// sprite property and enable the methods flipX, flipY. +/// +-(void)updateBar +{ + + float alpha = percentage_ / 100.f; + + CGPoint tMax = ccp(sprite_.texture.maxS,sprite_.texture.maxT); + + unsigned char vIndexes[2] = {0,0}; + unsigned char index = 0; + + // We know vertex data is always equal to the 4 corners + // If we don't have vertex data then we create it here and populate + // the side of the bar vertices that won't ever change. + if (!vertexData_) { + vertexDataCount_ = kProgressTextureCoordsCount; + vertexData_ = malloc(vertexDataCount_ * sizeof(ccV2F_C4B_T2F)); + NSAssert( vertexData_, @"CCProgressTimer. Not enough memory"); + + if(type_ == kCCProgressTimerTypeHorizontalBarLR){ + vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0,0}; + vertexData_[vIndexes[1] = 1].texCoords = (ccTex2F){0, tMax.y}; + }else if (type_ == kCCProgressTimerTypeHorizontalBarRL) { + vertexData_[vIndexes[0] = 2].texCoords = (ccTex2F){tMax.x, tMax.y}; + vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, 0.f}; + }else if (type_ == kCCProgressTimerTypeVerticalBarBT) { + vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){0, tMax.y}; + vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, tMax.y}; + }else if (type_ == kCCProgressTimerTypeVerticalBarTB) { + vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0, 0}; + vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x, 0}; + } + + index = vIndexes[0]; + vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)]; + + index = vIndexes[1]; + vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)]; + + if (sprite_.flipY || sprite_.flipX) { + if (sprite_.flipX) { + index = vIndexes[0]; + vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u; + index = vIndexes[1]; + vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u; + } + if(sprite_.flipY){ + index = vIndexes[0]; + vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v; + index = vIndexes[1]; + vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v; + } + } + + [self updateColor]; + } + + if(type_ == kCCProgressTimerTypeHorizontalBarLR){ + vertexData_[vIndexes[0] = 3].texCoords = (ccTex2F){tMax.x*alpha, tMax.y}; + vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x*alpha, 0}; + }else if (type_ == kCCProgressTimerTypeHorizontalBarRL) { + vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){tMax.x*(1.f - alpha), 0}; + vertexData_[vIndexes[1] = 0].texCoords = (ccTex2F){tMax.x*(1.f - alpha), tMax.y}; + }else if (type_ == kCCProgressTimerTypeVerticalBarBT) { + vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0, tMax.y*(1.f - alpha)}; + vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x, tMax.y*(1.f - alpha)}; + }else if (type_ == kCCProgressTimerTypeVerticalBarTB) { + vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){0, tMax.y*alpha}; + vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, tMax.y*alpha}; + } + + index = vIndexes[0]; + vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)]; + index = vIndexes[1]; + vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)]; + + if (sprite_.flipY || sprite_.flipX) { + if (sprite_.flipX) { + index = vIndexes[0]; + vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u; + index = vIndexes[1]; + vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u; + } + if(sprite_.flipY){ + index = vIndexes[0]; + vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v; + index = vIndexes[1]; + vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v; + } + } + +} + +-(CGPoint)boundaryTexCoord:(char)index +{ + if (index < kProgressTextureCoordsCount) { + switch (type_) { + case kCCProgressTimerTypeRadialCW: + return ccp((kProgressTextureCoords>>((index<<1)+1))&1,(kProgressTextureCoords>>(index<<1))&1); + case kCCProgressTimerTypeRadialCCW: + return ccp((kProgressTextureCoords>>(7-(index<<1)))&1,(kProgressTextureCoords>>(7-((index<<1)+1)))&1); + default: + break; + } + } + return CGPointZero; +} + +-(void)draw +{ + if(!vertexData_)return; + if(!sprite_)return; + ccBlendFunc blendFunc = sprite_.blendFunc; + BOOL newBlend = blendFunc.src != CC_BLEND_SRC || blendFunc.dst != CC_BLEND_DST; + if( newBlend ) + glBlendFunc( blendFunc.src, blendFunc.dst ); + + /// ======================================================================== + // Replaced [texture_ drawAtPoint:CGPointZero] with my own vertexData + // Everything above me and below me is copied from CCTextureNode's draw + glBindTexture(GL_TEXTURE_2D, sprite_.texture.name); + glVertexPointer(2, GL_FLOAT, sizeof(ccV2F_C4B_T2F), &vertexData_[0].vertices); + glTexCoordPointer(2, GL_FLOAT, sizeof(ccV2F_C4B_T2F), &vertexData_[0].texCoords); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ccV2F_C4B_T2F), &vertexData_[0].colors); + if(type_ == kCCProgressTimerTypeRadialCCW || type_ == kCCProgressTimerTypeRadialCW){ + glDrawArrays(GL_TRIANGLE_FAN, 0, vertexDataCount_); + } else if (type_ == kCCProgressTimerTypeHorizontalBarLR || + type_ == kCCProgressTimerTypeHorizontalBarRL || + type_ == kCCProgressTimerTypeVerticalBarBT || + type_ == kCCProgressTimerTypeVerticalBarTB) { + glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexDataCount_); + } + //glDrawElements(GL_TRIANGLES, indicesCount_, GL_UNSIGNED_BYTE, indices_); + /// ======================================================================== + + if( newBlend ) + glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); +} + +@end diff --git a/libs/cocos2d/CCProtocols.h b/libs/cocos2d/CCProtocols.h new file mode 100644 index 0000000..f40c1ab --- /dev/null +++ b/libs/cocos2d/CCProtocols.h @@ -0,0 +1,124 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "ccTypes.h" +#import "CCTexture2D.h" + +#pragma mark - +#pragma mark CCRGBAProtocol + +/// CC RGBA protocol +@protocol CCRGBAProtocol +/** sets Color + @since v0.8 + */ +-(void) setColor:(ccColor3B)color; +/** returns the color + @since v0.8 + */ +-(ccColor3B) color; + +/// returns the opacity +-(GLubyte) opacity; +/** sets the opacity. + @warning If the the texture has premultiplied alpha then, the R, G and B channels will be modifed. + Values goes from 0 to 255, where 255 means fully opaque. + */ +-(void) setOpacity: (GLubyte) opacity; +@optional +/** sets the premultipliedAlphaOpacity property. + If set to NO then opacity will be applied as: glColor(R,G,B,opacity); + If set to YES then oapcity will be applied as: glColor(opacity, opacity, opacity, opacity ); + Textures with premultiplied alpha will have this property by default on YES. Otherwise the default value is NO + @since v0.8 + */ +-(void) setOpacityModifyRGB:(BOOL)boolean; +/** returns whether or not the opacity will be applied using glColor(R,G,B,opacity) or glColor(opacity, opacity, opacity, opacity); + @since v0.8 + */ + -(BOOL) doesOpacityModifyRGB; +@end + +#pragma mark - +#pragma mark CCBlendProtocol +/** + You can specify the blending fuction. + @since v0.99.0 + */ +@protocol CCBlendProtocol +/** set the source blending function for the texture */ +-(void) setBlendFunc:(ccBlendFunc)blendFunc; +/** returns the blending function used for the texture */ +-(ccBlendFunc) blendFunc; +@end + + +#pragma mark - +#pragma mark CCTextureProtocol + +/** CCNode objects that uses a Texture2D to render the images. + The texture can have a blending function. + If the texture has alpha premultiplied the default blending function is: + src=GL_ONE dst= GL_ONE_MINUS_SRC_ALPHA + else + src=GL_SRC_ALPHA dst= GL_ONE_MINUS_SRC_ALPHA + But you can change the blending funtion at any time. + @since v0.8.0 + */ +@protocol CCTextureProtocol +/** returns the used texture */ +-(CCTexture2D*) texture; +/** sets a new texture. it will be retained */ +-(void) setTexture:(CCTexture2D*)texture; +@end + +#pragma mark - +#pragma mark CCLabelProtocol +/** Common interface for Labels */ +@protocol CCLabelProtocol +/** sets a new label using an NSString. + The string will be copied. + */ +-(void) setString:(NSString*)label; +/** returns the string that is rendered */ +-(NSString*) string; +@optional +/** sets a new label using a CString. + It is faster than setString since it doesn't require to alloc/retain/release an NString object. + @since v0.99.0 + */ +-(void) setCString:(char*)label; +@end + + +#pragma mark - +#pragma mark CCProjectionProtocol +/** OpenGL projection protocol */ +@protocol CCProjectionProtocol +/** Called by CCDirector when the porjection is updated, and "custom" projection is used + @since v0.99.5 + */ +-(void) updateProjection; +@end \ No newline at end of file diff --git a/libs/cocos2d/CCRenderTexture.h b/libs/cocos2d/CCRenderTexture.h new file mode 100755 index 0000000..d5e39cc --- /dev/null +++ b/libs/cocos2d/CCRenderTexture.h @@ -0,0 +1,108 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Jason Booth + * + * 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 +#import "CCNode.h" +#import "CCSprite.h" +#import "Support/OpenGL_Internal.h" + +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import +#endif // iPHone + +enum +{ + kCCImageFormatJPG = 0, + kCCImageFormatPNG = 1, + kCCImageFormatRawData =2 +}; + + +/** + CCRenderTexture is a generic rendering target. To render things into it, + simply construct a render target, call begin on it, call visit on any cocos + scenes or objects to render them, and call end. For convienience, render texture + adds a sprite as it's display child with the results, so you can simply add + the render texture to your scene and treat it like any other CocosNode. + There are also functions for saving the render texture to disk in PNG or JPG format. + + @since v0.8.1 + */ +@interface CCRenderTexture : CCNode +{ + GLuint fbo_; + GLint oldFBO_; + CCTexture2D* texture_; + CCSprite* sprite_; + + GLenum pixelFormat_; +} + +/** The CCSprite being used. + The sprite, by default, will use the following blending function: GL_ONE, GL_ONE_MINUS_SRC_ALPHA. + The blending function can be changed in runtime by calling: + - [[renderTexture sprite] setBlendFunc:(ccBlendFunc){GL_ONE, GL_ONE_MINUS_SRC_ALPHA}]; +*/ +@property (nonatomic,readwrite, assign) CCSprite* sprite; + +/** creates a RenderTexture object with width and height in Points and a pixel format, only RGB and RGBA formats are valid */ ++(id)renderTextureWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format; + +/** creates a RenderTexture object with width and height in Points, pixel format is RGBA8888 */ ++(id)renderTextureWithWidth:(int)w height:(int)h; + +/** initializes a RenderTexture object with width and height in Points and a pixel format, only RGB and RGBA formats are valid */ +-(id)initWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format; + +/** starts grabbing */ +-(void)begin; + +/** starts rendering to the texture while clearing the texture first. + This is more efficient then calling -clear first and then -begin */ +-(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a; + +/** ends grabbing */ +-(void)end; + +/** clears the texture with a color */ +-(void)clear:(float)r g:(float)g b:(float)b a:(float)a; + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +/** saves the texture into a file */ +-(BOOL)saveBuffer:(NSString*)name; +/** saves the texture into a file. The format can be JPG or PNG */ +-(BOOL)saveBuffer:(NSString*)name format:(int)format; +/* get buffer as UIImage, can only save a render buffer which has a RGBA8888 pixel format */ +-(NSData*)getUIImageAsDataFromBuffer:(int) format; +/* get buffer as UIImage */ +-(UIImage *)getUIImageFromBuffer; + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED + +@end + + diff --git a/libs/cocos2d/CCRenderTexture.m b/libs/cocos2d/CCRenderTexture.m new file mode 100755 index 0000000..4a4768e --- /dev/null +++ b/libs/cocos2d/CCRenderTexture.m @@ -0,0 +1,340 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Jason Booth + * + * 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 +#import "CCRenderTexture.h" +#import "CCDirector.h" +#import "ccMacros.h" +#import "Support/ccUtils.h" +#import "Support/CCFileUtils.h" + +@implementation CCRenderTexture + +@synthesize sprite=sprite_; + +// issue #994 ++(id)renderTextureWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format +{ + return [[[self alloc] initWithWidth:w height:h pixelFormat:format] autorelease]; +} + ++(id)renderTextureWithWidth:(int)w height:(int)h +{ + return [[[self alloc] initWithWidth:w height:h pixelFormat:kCCTexture2DPixelFormat_RGBA8888] autorelease]; +} + +-(id)initWithWidth:(int)w height:(int)h +{ + return [self initWithWidth:w height:h pixelFormat:kCCTexture2DPixelFormat_RGBA8888]; +} + +-(id)initWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format +{ + if ((self = [super init])) + { + NSAssert(format != kCCTexture2DPixelFormat_A8,@"only RGB and RGBA formats are valid for a render texture"); + + w *= CC_CONTENT_SCALE_FACTOR(); + h *= CC_CONTENT_SCALE_FACTOR(); + + glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO_); + + // textures must be power of two + NSUInteger powW = ccNextPOT(w); + NSUInteger powH = ccNextPOT(h); + + void *data = malloc((int)(powW * powH * 4)); + memset(data, 0, (int)(powW * powH * 4)); + pixelFormat_=format; + + texture_ = [[CCTexture2D alloc] initWithData:data pixelFormat:pixelFormat_ pixelsWide:powW pixelsHigh:powH contentSize:CGSizeMake(w, h)]; + free( data ); + + // generate FBO + ccglGenFramebuffers(1, &fbo_); + ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo_); + + // associate texture with FBO + ccglFramebufferTexture2D(CC_GL_FRAMEBUFFER, CC_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_.name, 0); + + // check if it worked (probably worth doing :) ) + GLuint status = ccglCheckFramebufferStatus(CC_GL_FRAMEBUFFER); + if (status != CC_GL_FRAMEBUFFER_COMPLETE) + { + [NSException raise:@"Render Texture" format:@"Could not attach texture to framebuffer"]; + } + [texture_ setAliasTexParameters]; + + sprite_ = [CCSprite spriteWithTexture:texture_]; + + [texture_ release]; + [sprite_ setScaleY:-1]; + [self addChild:sprite_]; + + // issue #937 + [sprite_ setBlendFunc:(ccBlendFunc){GL_ONE, GL_ONE_MINUS_SRC_ALPHA}]; + + ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO_); + } + return self; +} + +-(void)dealloc +{ +// [self removeAllChildrenWithCleanup:YES]; + ccglDeleteFramebuffers(1, &fbo_); + [super dealloc]; +} + +-(void)begin +{ + // Save the current matrix + glPushMatrix(); + + CGSize texSize = [texture_ contentSizeInPixels]; + + + // Calculate the adjustment ratios based on the old and new projections + CGSize size = [[CCDirector sharedDirector] displaySizeInPixels]; + float widthRatio = size.width / texSize.width; + float heightRatio = size.height / texSize.height; + + + // Adjust the orthographic propjection and viewport + ccglOrtho((float)-1.0 / widthRatio, (float)1.0 / widthRatio, (float)-1.0 / heightRatio, (float)1.0 / heightRatio, -1,1); + glViewport(0, 0, texSize.width, texSize.height); + + + glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO_); + ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo_);//Will direct drawing to the frame buffer created above + + // Issue #1145 + // There is no need to enable the default GL states here + // but since CCRenderTexture is mostly used outside the "render" loop + // these states needs to be enabled. + // Since this bug was discovered in API-freeze (very close of 1.0 release) + // This bug won't be fixed to prevent incompatibilities with code. + // + // If you understand the above mentioned message, then you can comment the following line + // and enable the gl states manually, in case you need them. + CC_ENABLE_DEFAULT_GL_STATES(); +} + +-(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a +{ + [self begin]; + + // save clear color + GLfloat clearColor[4]; + glGetFloatv(GL_COLOR_CLEAR_VALUE,clearColor); + + glClearColor(r, g, b, a); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // restore clear color + glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); +} + +-(void)end +{ + ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO_); + // Restore the original matrix and viewport + glPopMatrix(); + CGSize size = [[CCDirector sharedDirector] displaySizeInPixels]; + glViewport(0, 0, size.width, size.height); +} + +-(void)clear:(float)r g:(float)g b:(float)b a:(float)a +{ + [self beginWithClear:r g:g b:b a:a]; + [self end]; +} + +#pragma mark RenderTexture - Save Image + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +-(BOOL)saveBuffer:(NSString*)name +{ + return [self saveBuffer:name format:kCCImageFormatJPG]; +} + +-(BOOL)saveBuffer:(NSString*)fileName format:(int)format +{ + NSString *fullPath = [CCFileUtils fullPathFromRelativePath:fileName]; + + NSData *data = [self getUIImageAsDataFromBuffer:format]; + + return [data writeToFile:fullPath atomically:YES]; +} + +/* get buffer as UIImage */ +-(UIImage *)getUIImageFromBuffer +{ + NSAssert(pixelFormat_ == kCCTexture2DPixelFormat_RGBA8888,@"only RGBA8888 can be saved as image"); + + CGSize s = [texture_ contentSizeInPixels]; + int tx = s.width; + int ty = s.height; + + int bitsPerComponent = 8; + int bitsPerPixel = 32; + int bytesPerPixel = (bitsPerComponent * 4)/8; + int bytesPerRow = bytesPerPixel * tx; + NSInteger myDataLength = bytesPerRow * ty; + + NSMutableData *buffer = [[NSMutableData alloc] initWithCapacity:myDataLength]; + NSMutableData *pixels = [[NSMutableData alloc] initWithCapacity:myDataLength]; + + if( ! (buffer && pixels) ) { + CCLOG(@"cocos2d: CCRenderTexture#getUIImageFromBuffer: not enough memory"); + [buffer release]; + [pixels release]; + return nil; + } + + [self begin]; + glReadPixels(0,0,tx,ty,GL_RGBA,GL_UNSIGNED_BYTE, [buffer mutableBytes]); + [self end]; + + // make data provider with data. + + CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault; + CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, [buffer mutableBytes], myDataLength, NULL); + CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); + CGImageRef iref = CGImageCreate(tx, ty, + bitsPerComponent, bitsPerPixel, bytesPerRow, + colorSpaceRef, bitmapInfo, provider, + NULL, false, + kCGRenderingIntentDefault); + + CGContextRef context = CGBitmapContextCreate([pixels mutableBytes], tx, + ty, CGImageGetBitsPerComponent(iref), + CGImageGetBytesPerRow(iref), CGImageGetColorSpace(iref), + bitmapInfo); + CGContextTranslateCTM(context, 0.0f, ty); + CGContextScaleCTM(context, 1.0f, -1.0f); + CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, tx, ty), iref); + CGImageRef outputRef = CGBitmapContextCreateImage(context); + UIImage* image = [[UIImage alloc] initWithCGImage:outputRef]; + + CGImageRelease(iref); + CGContextRelease(context); + CGColorSpaceRelease(colorSpaceRef); + CGDataProviderRelease(provider); + CGImageRelease(outputRef); + + [pixels release]; + [buffer release]; + + return [image autorelease]; +} + +-(NSData*)getUIImageAsDataFromBuffer:(int) format +{ + NSAssert(pixelFormat_ == kCCTexture2DPixelFormat_RGBA8888,@"only RGBA8888 can be saved as image"); + + CGSize s = [texture_ contentSizeInPixels]; + int tx = s.width; + int ty = s.height; + + int bitsPerComponent=8; + int bitsPerPixel=32; + + int bytesPerRow = (bitsPerPixel/8) * tx; + NSInteger myDataLength = bytesPerRow * ty; + + GLubyte *buffer = malloc(sizeof(GLubyte)*myDataLength); + GLubyte *pixels = malloc(sizeof(GLubyte)*myDataLength); + + if( ! (buffer && pixels) ) { + CCLOG(@"cocos2d: CCRenderTexture#getUIImageFromBuffer: not enough memory"); + free(buffer); + free(pixels); + return nil; + } + + [self begin]; + glReadPixels(0,0,tx,ty,GL_RGBA,GL_UNSIGNED_BYTE, buffer); + [self end]; + + int x,y; + + for(y = 0; y +{ + NSMutableArray* segments_; + NSMutableArray* deletedSegments_; + + CGPoint lastPoint1_; + CGPoint lastPoint2_; + CGPoint lastLocation_; + int vertCount_; + float texVPos_; + float curTime_; + float fadeTime_; + float delta_; + float lastWidth_; + float lastSign_; + BOOL pastFirstPoint_; + + // Texture used + CCTexture2D* texture_; + + // texture length + float textureLength_; + + // RGBA protocol + ccColor4B color_; + + // blend func + ccBlendFunc blendFunc_; +} + +/** Texture used by the ribbon. Conforms to CCTextureProtocol protocol */ +@property (nonatomic,readwrite,retain) CCTexture2D* texture; + +/** Texture lengths in pixels */ +@property (nonatomic,readwrite) float textureLength; + +/** GL blendind function */ +@property (nonatomic,readwrite,assign) ccBlendFunc blendFunc; + +/** color used by the Ribbon (RGBA) */ +@property (nonatomic,readwrite) ccColor4B color; + +/** creates the ribbon */ ++(id)ribbonWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade; +/** init the ribbon */ +-(id)initWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade; +/** add a point to the ribbon */ +-(void)addPointAt:(CGPoint)location width:(float)w; +/** polling function */ +-(void)update:(ccTime)delta; +/** determine side of line */ +-(float)sideOfLine:(CGPoint)p l1:(CGPoint)l1 l2:(CGPoint)l2; + +@end + +/** object to hold ribbon segment data */ +@interface CCRibbonSegment : NSObject +{ +@public + GLfloat verts[50*6]; + GLfloat coords[50*4]; + GLubyte colors[50*8]; + float creationTime[50]; + BOOL finished; + uint end; + uint begin; +} +-(id)init; +-(void)reset; +-(void)draw:(float)curTime fadeTime:(float)fadeTime color:(ccColor4B)color; +@end diff --git a/libs/cocos2d/CCRibbon.m b/libs/cocos2d/CCRibbon.m new file mode 100644 index 0000000..9555455 --- /dev/null +++ b/libs/cocos2d/CCRibbon.m @@ -0,0 +1,381 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008, 2009 Jason Booth + * + * 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. + */ + +/* + * A ribbon is a dynamically generated list of polygons drawn as a single or series + * of triangle strips. The primary use of Ribbon is as the drawing class of Motion Streak, + * but it is quite useful on it's own. When manually drawing a ribbon, you can call addPointAt + * and pass in the parameters for the next location in the ribbon. The system will automatically + * generate new polygons, texture them accourding to your texture width, etc, etc. + * + * Ribbon data is stored in a RibbonSegment class. This class statically allocates enough verticies and + * texture coordinates for 50 locations (100 verts or 48 triangles). The ribbon class will allocate + * new segments when they are needed, and reuse old ones if available. The idea is to avoid constantly + * allocating new memory and prefer a more static method. However, since there is no way to determine + * the maximum size of some ribbons (motion streaks), a truely static allocation is not possible. + * + */ + + +#import "CCRibbon.h" +#import "CCTextureCache.h" +#import "Support/CGPointExtension.h" +#import "ccMacros.h" + +// +// Ribbon +// +@implementation CCRibbon +@synthesize blendFunc=blendFunc_; +@synthesize color=color_; +@synthesize textureLength = textureLength_; + ++(id)ribbonWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade +{ + self = [[[self alloc] initWithWidth:w image:path length:l color:color fade:fade] autorelease]; + return self; +} + +-(id)initWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade +{ + self = [super init]; + if (self) + { + + segments_ = [[NSMutableArray alloc] init]; + deletedSegments_ = [[NSMutableArray alloc] init]; + + /* 1 initial segment */ + CCRibbonSegment* seg = [[CCRibbonSegment alloc] init]; + [segments_ addObject:seg]; + [seg release]; + + textureLength_ = l; + + color_ = color; + fadeTime_ = fade; + lastLocation_ = CGPointZero; + lastWidth_ = w/2; + texVPos_ = 0.0f; + + curTime_ = 0; + pastFirstPoint_ = NO; + + /* XXX: + Ribbon, by default uses this blend function, which might not be correct + if you are using premultiplied alpha images, + but 99% you might want to use this blending function regarding of the texture + */ + blendFunc_.src = GL_SRC_ALPHA; + blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; + + self.texture = [[CCTextureCache sharedTextureCache] addImage:path]; + + /* default texture parameter */ + ccTexParams params = { GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT }; + [texture_ setTexParameters:¶ms]; + } + return self; +} + +-(void)dealloc +{ + [segments_ release]; + [deletedSegments_ release]; + [texture_ release]; + [super dealloc]; +} + +// rotates a point around 0, 0 +-(CGPoint)rotatePoint:(CGPoint)vec rotation:(float)a +{ + float xtemp = (vec.x * cosf(a)) - (vec.y * sinf(a)); + vec.y = (vec.x * sinf(a)) + (vec.y * cosf(a)); + vec.x = xtemp; + return vec; +} + +-(void)update:(ccTime)delta +{ + curTime_+= delta; + delta_ = delta; +} + +-(float)sideOfLine:(CGPoint)p l1:(CGPoint)l1 l2:(CGPoint)l2 +{ + CGPoint vp = ccpPerp(ccpSub(l1, l2)); + CGPoint vx = ccpSub(p, l1); + return ccpDot(vx, vp); +} + +// adds a new segment to the ribbon +-(void)addPointAt:(CGPoint)location width:(float)w +{ + location.x *= CC_CONTENT_SCALE_FACTOR(); + location.y *= CC_CONTENT_SCALE_FACTOR(); + + w = w*0.5f; + // if this is the first point added, cache it and return + if (!pastFirstPoint_) + { + lastWidth_ = w; + lastLocation_ = location; + pastFirstPoint_ = YES; + return; + } + + CGPoint sub = ccpSub(lastLocation_, location); + float r = ccpToAngle(sub) + (float)M_PI_2; + CGPoint p1 = ccpAdd([self rotatePoint:ccp(-w, 0) rotation:r], location); + CGPoint p2 = ccpAdd([self rotatePoint:ccp(w, 0) rotation:r], location); + float len = sqrtf(powf(lastLocation_.x - location.x, 2) + powf(lastLocation_.y - location.y, 2)); + float tend = texVPos_ + len/textureLength_; + CCRibbonSegment* seg; + // grab last segment + seg = [segments_ lastObject]; + // lets kill old segments + for (CCRibbonSegment* seg2 in segments_) + { + if (seg2 != seg && seg2->finished) + { + [deletedSegments_ addObject:seg2]; + } + } + [segments_ removeObjectsInArray:deletedSegments_]; + // is the segment full? + if (seg->end >= 50) + [segments_ removeObjectsInArray:deletedSegments_]; + // grab last segment and append to it if it's not full + seg = [segments_ lastObject]; + // is the segment full? + if (seg->end >= 50) + { + CCRibbonSegment* newSeg; + // grab it from the cache if we can + if ([deletedSegments_ count] > 0) + { + newSeg = [deletedSegments_ objectAtIndex:0]; + [newSeg retain]; // will be released later + [deletedSegments_ removeObject:newSeg]; + [newSeg reset]; + } + else + { + newSeg = [[CCRibbonSegment alloc] init]; // will be released later + } + + newSeg->creationTime[0] = seg->creationTime[seg->end - 1]; + int v = (seg->end-1)*6; + int c = (seg->end-1)*4; + newSeg->verts[0] = seg->verts[v]; + newSeg->verts[1] = seg->verts[v+1]; + newSeg->verts[2] = seg->verts[v+2]; + newSeg->verts[3] = seg->verts[v+3]; + newSeg->verts[4] = seg->verts[v+4]; + newSeg->verts[5] = seg->verts[v+5]; + + newSeg->coords[0] = seg->coords[c]; + newSeg->coords[1] = seg->coords[c+1]; + newSeg->coords[2] = seg->coords[c+2]; + newSeg->coords[3] = seg->coords[c+3]; + newSeg->end++; + seg = newSeg; + [segments_ addObject:seg]; + [newSeg release]; // it was retained before + + } + if (seg->end == 0) + { + // first edge has to get rotation from the first real polygon + CGPoint lp1 = ccpAdd([self rotatePoint:ccp(-lastWidth_, 0) rotation:r], lastLocation_); + CGPoint lp2 = ccpAdd([self rotatePoint:ccp(+lastWidth_, 0) rotation:r], lastLocation_); + seg->creationTime[0] = curTime_ - delta_; + seg->verts[0] = lp1.x; + seg->verts[1] = lp1.y; + seg->verts[2] = 0.0f; + seg->verts[3] = lp2.x; + seg->verts[4] = lp2.y; + seg->verts[5] = 0.0f; + seg->coords[0] = 0.0f; + seg->coords[1] = texVPos_; + seg->coords[2] = 1.0f; + seg->coords[3] = texVPos_; + seg->end++; + } + + int v = seg->end*6; + int c = seg->end*4; + // add new vertex + seg->creationTime[seg->end] = curTime_; + seg->verts[v] = p1.x; + seg->verts[v+1] = p1.y; + seg->verts[v+2] = 0.0f; + seg->verts[v+3] = p2.x; + seg->verts[v+4] = p2.y; + seg->verts[v+5] = 0.0f; + + + seg->coords[c] = 0.0f; + seg->coords[c+1] = tend; + seg->coords[c+2] = 1.0f; + seg->coords[c+3] = tend; + + texVPos_ = tend; + lastLocation_ = location; + lastPoint1_ = p1; + lastPoint2_ = p2; + lastWidth_ = w; + seg->end++; +} + +-(void) draw +{ + if ([segments_ count] > 0) + { + // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY + // Unneeded states: GL_COLOR_ARRAY + glDisableClientState(GL_COLOR_ARRAY); + + glBindTexture(GL_TEXTURE_2D, [texture_ name]); + + BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST; + if( newBlend ) + glBlendFunc( blendFunc_.src, blendFunc_.dst ); + + for (CCRibbonSegment* seg in segments_) + [seg draw:curTime_ fadeTime:fadeTime_ color:color_]; + + if( newBlend ) + glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + + // restore default GL state + glEnableClientState( GL_COLOR_ARRAY ); + } +} + +#pragma mark Ribbon - CocosNodeTexture protocol +-(void) setTexture:(CCTexture2D*) texture +{ + [texture_ release]; + texture_ = [texture retain]; + [self setContentSizeInPixels: texture.contentSizeInPixels]; + /* XXX Don't update blending function in Ribbons */ +} + +-(CCTexture2D*) texture +{ + return texture_; +} + +@end + + +#pragma mark - +#pragma mark RibbonSegment + +@implementation CCRibbonSegment + +-(id)init +{ + self = [super init]; + if (self) + { + [self reset]; + } + return self; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | end = %i, begin = %i>", [self class], self, end, begin]; +} + +- (void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + [super dealloc]; +} + +-(void)reset +{ + end = 0; + begin = 0; + finished = NO; +} + +-(void)draw:(float)curTime fadeTime:(float)fadeTime color:(ccColor4B)color +{ + GLubyte r = color.r; + GLubyte g = color.g; + GLubyte b = color.b; + GLubyte a = color.a; + + if (begin < 50) + { + // the motion streak class will call update and cause time to change, thus, if curTime_ != 0 + // we have to generate alpha for the ribbon each frame. + if (curTime == 0) + { + // no alpha over time, so just set the color + glColor4ub(r,g,b,a); + } + else + { + // generate alpha/color for each point + glEnableClientState(GL_COLOR_ARRAY); + uint i = begin; + for (; i < end; ++i) + { + int idx = i*8; + colors[idx] = r; + colors[idx+1] = g; + colors[idx+2] = b; + colors[idx+4] = r; + colors[idx+5] = g; + colors[idx+6] = b; + float alive = ((curTime - creationTime[i]) / fadeTime); + if (alive > 1) + { + begin++; + colors[idx+3] = 0; + colors[idx+7] = 0; + } + else + { + colors[idx+3] = (GLubyte)(255.f - (alive * 255.f)); + colors[idx+7] = colors[idx+3]; + } + } + glColorPointer(4, GL_UNSIGNED_BYTE, 0, &colors[begin*8]); + } + glVertexPointer(3, GL_FLOAT, 0, &verts[begin*6]); + glTexCoordPointer(2, GL_FLOAT, 0, &coords[begin*4]); + glDrawArrays(GL_TRIANGLE_STRIP, 0, (end - begin) * 2); + } + else + finished = YES; +} +@end + diff --git a/libs/cocos2d/CCScene.h b/libs/cocos2d/CCScene.h new file mode 100644 index 0000000..eedc985 --- /dev/null +++ b/libs/cocos2d/CCScene.h @@ -0,0 +1,42 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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" + +/** CCScene is a subclass of CCNode that is used only as an abstract concept. + + CCScene an CCNode are almost identical with the difference that CCScene has it's + anchor point (by default) at the center of the screen. + + For the moment CCScene has no other logic than that, but in future releases it might have + additional logic. + + It is a good practice to use and CCScene as the parent of all your nodes. +*/ +@interface CCScene : CCNode +{ +} +@end diff --git a/libs/cocos2d/CCScene.m b/libs/cocos2d/CCScene.m new file mode 100644 index 0000000..e1716bb --- /dev/null +++ b/libs/cocos2d/CCScene.m @@ -0,0 +1,44 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CCScene.h" +#import "Support/CGPointExtension.h" +#import "CCDirector.h" + + +@implementation CCScene +-(id) init +{ + if( (self=[super init]) ) { + CGSize s = [[CCDirector sharedDirector] winSize]; + self.isRelativeAnchorPoint = NO; + anchorPoint_ = ccp(0.5f, 0.5f); + [self setContentSize:s]; + } + + return self; +} +@end diff --git a/libs/cocos2d/CCScheduler.h b/libs/cocos2d/CCScheduler.h new file mode 100644 index 0000000..4f93a06 --- /dev/null +++ b/libs/cocos2d/CCScheduler.h @@ -0,0 +1,193 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "Support/uthash.h" +#import "ccTypes.h" + +typedef void (*TICK_IMP)(id, SEL, ccTime); + +// +// CCTimer +// +/** Light weight timer */ +@interface CCTimer : NSObject +{ + id target; + TICK_IMP impMethod; + + ccTime elapsed; + +@public // optimization + ccTime interval; + SEL selector; +} + +/** interval in seconds */ +@property (nonatomic,readwrite,assign) ccTime interval; + +/** Allocates a timer with a target and a selector. +*/ ++(id) timerWithTarget:(id) t selector:(SEL)s; + +/** Allocates a timer with a target, a selector and an interval in seconds. +*/ ++(id) timerWithTarget:(id) t selector:(SEL)s interval:(ccTime)seconds; + +/** Initializes a timer with a target and a selector. +*/ + -(id) initWithTarget:(id) t selector:(SEL)s; + +/** Initializes a timer with a target, a selector and an interval in seconds. +*/ +-(id) initWithTarget:(id) t selector:(SEL)s interval:(ccTime)seconds; + + +/** triggers the timer */ +-(void) update: (ccTime) dt; +@end + + + +// +// CCScheduler +// +/** Scheduler is responsible of triggering the scheduled callbacks. + You should not use NSTimer. Instead use this class. + + There are 2 different types of callbacks (selectors): + + - update selector: the 'update' selector will be called every frame. You can customize the priority. + - custom selector: A custom selector will be called every frame, or with a custom interval of time + + The 'custom selectors' should be avoided when possible. It is faster, and consumes less memory to use the 'update selector'. + +*/ + +struct _listEntry; +struct _hashSelectorEntry; +struct _hashUpdateEntry; + +@interface CCScheduler : NSObject +{ + ccTime timeScale_; + + // + // "updates with priority" stuff + // + struct _listEntry *updatesNeg; // list of priority < 0 + struct _listEntry *updates0; // list priority == 0 + struct _listEntry *updatesPos; // list priority > 0 + struct _hashUpdateEntry *hashForUpdates; // hash used to fetch quickly the list entries for pause,delete,etc. + + // Used for "selectors with interval" + struct _hashSelectorEntry *hashForSelectors; + struct _hashSelectorEntry *currentTarget; + BOOL currentTargetSalvaged; + + // Optimization + TICK_IMP impMethod; + SEL updateSelector; + + BOOL updateHashLocked; // If true unschedule will not remove anything from a hash. Elements will only be marked for deletion. +} + +/** Modifies the time of all scheduled callbacks. + You can use this property to create a 'slow motion' or 'fast fordward' effect. + Default is 1.0. To create a 'slow motion' effect, use values below 1.0. + To create a 'fast fordward' effect, use values higher than 1.0. + @since v0.8 + @warning It will affect EVERY scheduled selector / action. + */ +@property (nonatomic,readwrite) ccTime timeScale; + +/** returns a shared instance of the Scheduler */ ++(CCScheduler *)sharedScheduler; + +/** purges the shared scheduler. It releases the retained instance. + @since v0.99.0 + */ ++(void)purgeSharedScheduler; + +/** 'tick' the scheduler. + You should NEVER call this method, unless you know what you are doing. + */ +-(void) tick:(ccTime)dt; + +/** The scheduled method will be called every 'interval' seconds. + If paused is YES, then it won't be called until it is resumed. + If 'interval' is 0, it will be called every frame, but if so, it recommened to use 'scheduleUpdateForTarget:' instead. + If the selector is already scheduled, then only the interval parameter will be updated without re-scheduling it again. + + @since v0.99.3 + */ +-(void) scheduleSelector:(SEL)selector forTarget:(id)target interval:(ccTime)interval paused:(BOOL)paused; + +/** Schedules the 'update' selector for a given target with a given priority. + The 'update' selector will be called every frame. + The lower the priority, the earlier it is called. + @since v0.99.3 + */ +-(void) scheduleUpdateForTarget:(id)target priority:(NSInteger)priority paused:(BOOL)paused; + +/** Unshedules a selector for a given target. + If you want to unschedule the "update", use unscheudleUpdateForTarget. + @since v0.99.3 + */ +-(void) unscheduleSelector:(SEL)selector forTarget:(id)target; + +/** Unschedules the update selector for a given target + @since v0.99.3 + */ +-(void) unscheduleUpdateForTarget:(id)target; + +/** Unschedules all selectors for a given target. + This also includes the "update" selector. + @since v0.99.3 + */ +-(void) unscheduleAllSelectorsForTarget:(id)target; + +/** Unschedules all selectors from all targets. + You should NEVER call this method, unless you know what you are doing. + + @since v0.99.3 + */ +-(void) unscheduleAllSelectors; + +/** Pauses the target. + All scheduled selectors/update for a given target won't be 'ticked' until the target is resumed. + If the target is not present, nothing happens. + @since v0.99.3 + */ +-(void) pauseTarget:(id)target; + +/** Resumes the target. + The 'target' will be unpaused, so all schedule selectors/update will be 'ticked' again. + If the target is not present, nothing happens. + @since v0.99.3 + */ +-(void) resumeTarget:(id)target; + +@end diff --git a/libs/cocos2d/CCScheduler.m b/libs/cocos2d/CCScheduler.m new file mode 100644 index 0000000..9e4d442 --- /dev/null +++ b/libs/cocos2d/CCScheduler.m @@ -0,0 +1,641 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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. + */ + + +// cocos2d imports +#import "CCScheduler.h" +#import "ccMacros.h" +#import "Support/uthash.h" +#import "Support/utlist.h" +#import "Support/ccCArray.h" + +// +// Data structures +// +#pragma mark - +#pragma mark Data Structures + +// A list double-linked list used for "updates with priority" +typedef struct _listEntry +{ + struct _listEntry *prev, *next; + TICK_IMP impMethod; + id target; // not retained (retained by hashUpdateEntry) + NSInteger priority; + BOOL paused; + BOOL markedForDeletion; // selector will no longer be called and entry will be removed at end of the next tick +} tListEntry; + +typedef struct _hashUpdateEntry +{ + tListEntry **list; // Which list does it belong to ? + tListEntry *entry; // entry in the list + id target; // hash key (retained) + UT_hash_handle hh; +} tHashUpdateEntry; + +// Hash Element used for "selectors with interval" +typedef struct _hashSelectorEntry +{ + struct ccArray *timers; + id target; // hash key (retained) + unsigned int timerIndex; + CCTimer *currentTimer; + BOOL currentTimerSalvaged; + BOOL paused; + UT_hash_handle hh; +} tHashSelectorEntry; + + + +// +// CCTimer +// +#pragma mark - +#pragma mark - CCTimer + +@implementation CCTimer + +@synthesize interval; + +-(id) init +{ + NSAssert(NO, @"CCTimer: Init not supported."); + [self release]; + return nil; +} + ++(id) timerWithTarget:(id)t selector:(SEL)s +{ + return [[[self alloc] initWithTarget:t selector:s] autorelease]; +} + ++(id) timerWithTarget:(id)t selector:(SEL)s interval:(ccTime) i +{ + return [[[self alloc] initWithTarget:t selector:s interval:i] autorelease]; +} + +-(id) initWithTarget:(id)t selector:(SEL)s +{ + return [self initWithTarget:t selector:s interval:0]; +} + +-(id) initWithTarget:(id)t selector:(SEL)s interval:(ccTime) seconds +{ + if( (self=[super init]) ) { +#if COCOS2D_DEBUG + NSMethodSignature *sig = [t methodSignatureForSelector:s]; + NSAssert(sig !=0 , @"Signature not found for selector - does it have the following form? -(void) name: (ccTime) dt"); +#endif + + // target is not retained. It is retained in the hash structure + target = t; + selector = s; + impMethod = (TICK_IMP) [t methodForSelector:s]; + elapsed = -1; + interval = seconds; + } + return self; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | target:%@ selector:(%@)>", [self class], self, [target class], NSStringFromSelector(selector)]; +} + +-(void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + [super dealloc]; +} + +-(void) update: (ccTime) dt +{ + if( elapsed == - 1) + elapsed = 0; + else + elapsed += dt; + if( elapsed >= interval ) { + impMethod(target, selector, elapsed); + elapsed = 0; + } +} +@end + +// +// CCScheduler +// +#pragma mark - +#pragma mark - CCScheduler + +@interface CCScheduler (Private) +-(void) removeHashElement:(tHashSelectorEntry*)element; +@end + +@implementation CCScheduler + +static CCScheduler *sharedScheduler; + +@synthesize timeScale = timeScale_; + ++ (CCScheduler *)sharedScheduler +{ + if (!sharedScheduler) + sharedScheduler = [[CCScheduler alloc] init]; + + return sharedScheduler; +} + ++(id)alloc +{ + NSAssert(sharedScheduler == nil, @"Attempted to allocate a second instance of a singleton."); + return [super alloc]; +} + ++(void)purgeSharedScheduler +{ + [sharedScheduler release]; + sharedScheduler = nil; +} + +- (id) init +{ + if( (self=[super init]) ) { + timeScale_ = 1.0f; + + // used to trigger CCTimer#update + updateSelector = @selector(update:); + impMethod = (TICK_IMP) [CCTimer instanceMethodForSelector:updateSelector]; + + // updates with priority + updates0 = NULL; + updatesNeg = NULL; + updatesPos = NULL; + hashForUpdates = NULL; + + // selectors with interval + currentTarget = nil; + currentTargetSalvaged = NO; + hashForSelectors = nil; + updateHashLocked = NO; + } + + return self; +} + +- (void) dealloc +{ + CCLOG(@"cocos2d: deallocing %@", self); + + [self unscheduleAllSelectors]; + + sharedScheduler = nil; + + [super dealloc]; +} + + +#pragma mark CCScheduler - Custom Selectors + +-(void) removeHashElement:(tHashSelectorEntry*)element +{ + ccArrayFree(element->timers); + [element->target release]; + HASH_DEL(hashForSelectors, element); + free(element); +} + +-(void) scheduleSelector:(SEL)selector forTarget:(id)target interval:(ccTime)interval paused:(BOOL)paused +{ + NSAssert( selector != nil, @"Argument selector must be non-nil"); + NSAssert( target != nil, @"Argument target must be non-nil"); + + tHashSelectorEntry *element = NULL; + HASH_FIND_INT(hashForSelectors, &target, element); + + if( ! element ) { + element = calloc( sizeof( *element ), 1 ); + element->target = [target retain]; + HASH_ADD_INT( hashForSelectors, target, element ); + + // Is this the 1st element ? Then set the pause level to all the selectors of this target + element->paused = paused; + + } else + NSAssert( element->paused == paused, @"CCScheduler. Trying to schedule a selector with a pause value different than the target"); + + + if( element->timers == nil ) + element->timers = ccArrayNew(10); + else + { + for( unsigned int i=0; i< element->timers->num; i++ ) { + CCTimer *timer = element->timers->arr[i]; + if( selector == timer->selector ) { + CCLOG(@"CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.2f to %.2f", timer->interval, interval); + timer->interval = interval; + return; + } + } + ccArrayEnsureExtraCapacity(element->timers, 1); + } + + CCTimer *timer = [[CCTimer alloc] initWithTarget:target selector:selector interval:interval]; + ccArrayAppendObject(element->timers, timer); + [timer release]; +} + +-(void) unscheduleSelector:(SEL)selector forTarget:(id)target +{ + // explicity handle nil arguments when removing an object + if( target==nil && selector==NULL) + return; + + NSAssert( target != nil, @"Target MUST not be nil"); + NSAssert( selector != NULL, @"Selector MUST not be NULL"); + + tHashSelectorEntry *element = NULL; + HASH_FIND_INT(hashForSelectors, &target, element); + + if( element ) { + + for( unsigned int i=0; i< element->timers->num; i++ ) { + CCTimer *timer = element->timers->arr[i]; + + + if( selector == timer->selector ) { + + if( timer == element->currentTimer && !element->currentTimerSalvaged ) { + [element->currentTimer retain]; + element->currentTimerSalvaged = YES; + } + + ccArrayRemoveObjectAtIndex(element->timers, i ); + + // update timerIndex in case we are in tick:, looping over the actions + if( element->timerIndex >= i ) + element->timerIndex--; + + if( element->timers->num == 0 ) { + if( currentTarget == element ) + currentTargetSalvaged = YES; + else + [self removeHashElement: element]; + } + return; + } + } + } + + // Not Found +// NSLog(@"CCScheduler#unscheduleSelector:forTarget: selector not found: %@", selString); + +} + +#pragma mark CCScheduler - Update Specific + +-(void) priorityIn:(tListEntry**)list target:(id)target priority:(NSInteger)priority paused:(BOOL)paused +{ + tListEntry *listElement = malloc( sizeof(*listElement) ); + + listElement->target = target; + listElement->priority = priority; + listElement->paused = paused; + listElement->impMethod = (TICK_IMP) [target methodForSelector:updateSelector]; + listElement->next = listElement->prev = NULL; + listElement->markedForDeletion = NO; + + // empty list ? + if( ! *list ) { + DL_APPEND( *list, listElement ); + + } else { + BOOL added = NO; + + for( tListEntry *elem = *list; elem ; elem = elem->next ) { + if( priority < elem->priority ) { + + if( elem == *list ) + DL_PREPEND(*list, listElement); + else { + listElement->next = elem; + listElement->prev = elem->prev; + + elem->prev->next = listElement; + elem->prev = listElement; + } + + added = YES; + break; + } + } + + // Not added? priority has the higher value. Append it. + if( !added ) + DL_APPEND(*list, listElement); + } + + // update hash entry for quicker access + tHashUpdateEntry *hashElement = calloc( sizeof(*hashElement), 1 ); + hashElement->target = [target retain]; + hashElement->list = list; + hashElement->entry = listElement; + HASH_ADD_INT(hashForUpdates, target, hashElement ); +} + +-(void) appendIn:(tListEntry**)list target:(id)target paused:(BOOL)paused +{ + tListEntry *listElement = malloc( sizeof( * listElement ) ); + + listElement->target = target; + listElement->paused = paused; + listElement->markedForDeletion = NO; + listElement->impMethod = (TICK_IMP) [target methodForSelector:updateSelector]; + + DL_APPEND(*list, listElement); + + + // update hash entry for quicker access + tHashUpdateEntry *hashElement = calloc( sizeof(*hashElement), 1 ); + hashElement->target = [target retain]; + hashElement->list = list; + hashElement->entry = listElement; + HASH_ADD_INT(hashForUpdates, target, hashElement ); +} + +-(void) scheduleUpdateForTarget:(id)target priority:(NSInteger)priority paused:(BOOL)paused +{ + tHashUpdateEntry * hashElement = NULL; + HASH_FIND_INT(hashForUpdates, &target, hashElement); + if(hashElement) + { +#if COCOS2D_DEBUG >= 1 + NSAssert( hashElement->entry->markedForDeletion, @"CCScheduler: You can't re-schedule an 'update' selector'. Unschedule it first"); +#endif + // TODO : check if priority has changed! + + hashElement->entry->markedForDeletion = NO; + return; + } + + // most of the updates are going to be 0, that's way there + // is an special list for updates with priority 0 + if( priority == 0 ) + [self appendIn:&updates0 target:target paused:paused]; + + else if( priority < 0 ) + [self priorityIn:&updatesNeg target:target priority:priority paused:paused]; + + else // priority > 0 + [self priorityIn:&updatesPos target:target priority:priority paused:paused]; +} + +- (void) removeUpdateFromHash:(tListEntry*)entry +{ + tHashUpdateEntry * element = NULL; + + HASH_FIND_INT(hashForUpdates, &entry->target, element); + if( element ) { + // list entry + DL_DELETE( *element->list, element->entry ); + free( element->entry ); + + // hash entry + [element->target release]; + HASH_DEL( hashForUpdates, element); + free(element); + } +} + +-(void) unscheduleUpdateForTarget:(id)target +{ + if( target == nil ) + return; + + tHashUpdateEntry * element = NULL; + HASH_FIND_INT(hashForUpdates, &target, element); + if( element ) { + if(updateHashLocked) + element->entry->markedForDeletion = YES; + else + [self removeUpdateFromHash:element->entry]; + +// // list entry +// DL_DELETE( *element->list, element->entry ); +// free( element->entry ); +// +// // hash entry +// [element->target release]; +// HASH_DEL( hashForUpdates, element); +// free(element); + } +} + +#pragma mark CCScheduler - Common for Update selector & Custom Selectors + +-(void) unscheduleAllSelectors +{ + // Custom Selectors + for(tHashSelectorEntry *element=hashForSelectors; element != NULL; ) { + id target = element->target; + element=element->hh.next; + [self unscheduleAllSelectorsForTarget:target]; + } + + // Updates selectors + tListEntry *entry, *tmp; + DL_FOREACH_SAFE( updates0, entry, tmp ) { + [self unscheduleUpdateForTarget:entry->target]; + } + DL_FOREACH_SAFE( updatesNeg, entry, tmp ) { + [self unscheduleUpdateForTarget:entry->target]; + } + DL_FOREACH_SAFE( updatesPos, entry, tmp ) { + [self unscheduleUpdateForTarget:entry->target]; + } + +} + +-(void) unscheduleAllSelectorsForTarget:(id)target +{ + // explicit nil handling + if( target == nil ) + return; + + // Custom Selectors + tHashSelectorEntry *element = NULL; + HASH_FIND_INT(hashForSelectors, &target, element); + + if( element ) { + if( ccArrayContainsObject(element->timers, element->currentTimer) && !element->currentTimerSalvaged ) { + [element->currentTimer retain]; + element->currentTimerSalvaged = YES; + } + ccArrayRemoveAllObjects(element->timers); + if( currentTarget == element ) + currentTargetSalvaged = YES; + else + [self removeHashElement:element]; + } + + // Update Selector + [self unscheduleUpdateForTarget:target]; +} + +-(void) resumeTarget:(id)target +{ + NSAssert( target != nil, @"target must be non nil" ); + + // Custom Selectors + tHashSelectorEntry *element = NULL; + HASH_FIND_INT(hashForSelectors, &target, element); + if( element ) + element->paused = NO; + + // Update selector + tHashUpdateEntry * elementUpdate = NULL; + HASH_FIND_INT(hashForUpdates, &target, elementUpdate); + if( elementUpdate ) { + NSAssert( elementUpdate->entry != NULL, @"resumeTarget: unknown error"); + elementUpdate->entry->paused = NO; + } +} + +-(void) pauseTarget:(id)target +{ + NSAssert( target != nil, @"target must be non nil" ); + + // Custom selectors + tHashSelectorEntry *element = NULL; + HASH_FIND_INT(hashForSelectors, &target, element); + if( element ) + element->paused = YES; + + // Update selector + tHashUpdateEntry * elementUpdate = NULL; + HASH_FIND_INT(hashForUpdates, &target, elementUpdate); + if( elementUpdate ) { + NSAssert( elementUpdate->entry != NULL, @"pauseTarget: unknown error"); + elementUpdate->entry->paused = YES; + } + +} + +#pragma mark CCScheduler - Main Loop + +-(void) tick: (ccTime) dt +{ + updateHashLocked = YES; + + if( timeScale_ != 1.0f ) + dt *= timeScale_; + + // Iterate all over the Updates selectors + tListEntry *entry, *tmp; + + // updates with priority < 0 + DL_FOREACH_SAFE( updatesNeg, entry, tmp ) { + if( ! entry->paused && !entry->markedForDeletion ) + entry->impMethod( entry->target, updateSelector, dt ); + } + + // updates with priority == 0 + DL_FOREACH_SAFE( updates0, entry, tmp ) { + if( ! entry->paused && !entry->markedForDeletion ) + { + entry->impMethod( entry->target, updateSelector, dt ); + } + } + + // updates with priority > 0 + DL_FOREACH_SAFE( updatesPos, entry, tmp ) { + if( ! entry->paused && !entry->markedForDeletion ) + entry->impMethod( entry->target, updateSelector, dt ); + } + + // Iterate all over the custome selectors + for(tHashSelectorEntry *elt=hashForSelectors; elt != NULL; ) { + + currentTarget = elt; + currentTargetSalvaged = NO; + + if( ! currentTarget->paused ) { + + // The 'timers' ccArray may change while inside this loop. + for( elt->timerIndex = 0; elt->timerIndex < elt->timers->num; elt->timerIndex++) { + elt->currentTimer = elt->timers->arr[elt->timerIndex]; + elt->currentTimerSalvaged = NO; + + impMethod( elt->currentTimer, updateSelector, dt); + + if( elt->currentTimerSalvaged ) { + // The currentTimer told the remove itself. To prevent the timer from + // accidentally deallocating itself before finishing its step, we retained + // it. Now that step is done, it's safe to release it. + [elt->currentTimer release]; + } + + elt->currentTimer = nil; + } + } + + // elt, at this moment, is still valid + // so it is safe to ask this here (issue #490) + elt = elt->hh.next; + + // only delete currentTarget if no actions were scheduled during the cycle (issue #481) + if( currentTargetSalvaged && currentTarget->timers->num == 0 ) + [self removeHashElement:currentTarget]; + } + + // delete all updates that are morked for deletion + // updates with priority < 0 + DL_FOREACH_SAFE( updatesNeg, entry, tmp ) { + if(entry->markedForDeletion ) + { + [self removeUpdateFromHash:entry]; + } + } + + // updates with priority == 0 + DL_FOREACH_SAFE( updates0, entry, tmp ) { + if(entry->markedForDeletion ) + { + [self removeUpdateFromHash:entry]; + } + } + + // updates with priority > 0 + DL_FOREACH_SAFE( updatesPos, entry, tmp ) { + if(entry->markedForDeletion ) + { + [self removeUpdateFromHash:entry]; + } + } + + updateHashLocked = NO; + currentTarget = nil; +} +@end + diff --git a/libs/cocos2d/CCSprite.h b/libs/cocos2d/CCSprite.h new file mode 100644 index 0000000..2c7b82b --- /dev/null +++ b/libs/cocos2d/CCSprite.h @@ -0,0 +1,351 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CCProtocols.h" +#import "CCTextureAtlas.h" + +@class CCSpriteBatchNode; +@class CCSpriteFrame; +@class CCAnimation; + +#pragma mark CCSprite + +enum { + /// CCSprite invalid index on the CCSpriteBatchode + CCSpriteIndexNotInitialized = 0xffffffff, +}; + +/** + Whether or not an CCSprite will rotate, scale or translate with it's parent. + Useful in health bars, when you want that the health bar translates with it's parent but you don't + want it to rotate with its parent. + @since v0.99.0 + */ +typedef enum { + //! Translate with it's parent + CC_HONOR_PARENT_TRANSFORM_TRANSLATE = 1 << 0, + //! Rotate with it's parent + CC_HONOR_PARENT_TRANSFORM_ROTATE = 1 << 1, + //! Scale with it's parent + CC_HONOR_PARENT_TRANSFORM_SCALE = 1 << 2, + + //! All possible transformation enabled. Default value. + CC_HONOR_PARENT_TRANSFORM_ALL = CC_HONOR_PARENT_TRANSFORM_TRANSLATE | CC_HONOR_PARENT_TRANSFORM_ROTATE | CC_HONOR_PARENT_TRANSFORM_SCALE, + +} ccHonorParentTransform; + +/** CCSprite is a 2d image ( http://en.wikipedia.org/wiki/Sprite_(computer_graphics) ) + * + * CCSprite can be created with an image, or with a sub-rectangle of an image. + * + * If the parent or any of its ancestors is a CCSpriteBatchNode then the following features/limitations are valid + * - Features when the parent is a CCBatchNode: + * - MUCH faster rendering, specially if the CCSpriteBatchNode has many children. All the children will be drawn in a single batch. + * + * - Limitations + * - Camera is not supported yet (eg: CCOrbitCamera action doesn't work) + * - GridBase actions are not supported (eg: CCLens, CCRipple, CCTwirl) + * - The Alias/Antialias property belongs to CCSpriteBatchNode, so you can't individually set the aliased property. + * - The Blending function property belongs to CCSpriteBatchNode, so you can't individually set the blending function property. + * - Parallax scroller is not supported, but can be simulated with a "proxy" sprite. + * + * If the parent is an standard CCNode, then CCSprite behaves like any other CCNode: + * - It supports blending functions + * - It supports aliasing / antialiasing + * - But the rendering will be slower: 1 draw per children. + * + * The default anchorPoint in CCSprite is (0.5, 0.5). + */ +@interface CCSprite : CCNode +{ + + // + // Data used when the sprite is rendered using a CCSpriteBatchNode + // + CCTextureAtlas *textureAtlas_; // Sprite Sheet texture atlas (weak reference) + NSUInteger atlasIndex_; // Absolute (real) Index on the batch node + CCSpriteBatchNode *batchNode_; // Used batch node (weak reference) + ccHonorParentTransform honorParentTransform_; // whether or not to transform according to its parent transformations + BOOL dirty_; // Sprite needs to be updated + BOOL recursiveDirty_; // Subchildren needs to be updated + BOOL hasChildren_; // optimization to check if it contain children + + // + // Data used when the sprite is self-rendered + // + ccBlendFunc blendFunc_; // Needed for the texture protocol + CCTexture2D *texture_; // Texture used to render the sprite + + // + // Shared data + // + + // whether or not it's parent is a CCSpriteBatchNode + BOOL usesBatchNode_; + + // texture + CGRect rect_; + CGRect rectInPixels_; + BOOL rectRotated_; + + // Offset Position (used by Zwoptex) + CGPoint offsetPositionInPixels_; + CGPoint unflippedOffsetPositionFromCenter_; + + // vertex coords, texture coords and color info + ccV3F_C4B_T2F_Quad quad_; + + // opacity and RGB protocol + GLubyte opacity_; + ccColor3B color_; + ccColor3B colorUnmodified_; + BOOL opacityModifyRGB_; + + // image is flipped + BOOL flipX_; + BOOL flipY_; + + + // Animations that belong to the sprite + NSMutableDictionary *animations_; + +@public + // used internally. + void (*updateMethod)(id, SEL); +} + +/** whether or not the Sprite needs to be updated in the Atlas */ +@property (nonatomic,readwrite) BOOL dirty; +/** the quad (tex coords, vertex coords and color) information */ +@property (nonatomic,readonly) ccV3F_C4B_T2F_Quad quad; +/** The index used on the TextureAtlas. Don't modify this value unless you know what you are doing */ +@property (nonatomic,readwrite) NSUInteger atlasIndex; +/** returns the rect of the CCSprite in points */ +@property (nonatomic,readonly) CGRect textureRect; +/** returns whether or not the texture rectangle is rotated */ +@property (nonatomic,readonly) BOOL textureRectRotated; +/** whether or not the sprite is flipped horizontally. + It only flips the texture of the sprite, and not the texture of the sprite's children. + Also, flipping the texture doesn't alter the anchorPoint. + If you want to flip the anchorPoint too, and/or to flip the children too use: + + sprite.scaleX *= -1; + */ +@property (nonatomic,readwrite) BOOL flipX; +/** whether or not the sprite is flipped vertically. + It only flips the texture of the sprite, and not the texture of the sprite's children. + Also, flipping the texture doesn't alter the anchorPoint. + If you want to flip the anchorPoint too, and/or to flip the children too use: + + sprite.scaleY *= -1; + */ +@property (nonatomic,readwrite) BOOL flipY; +/** opacity: conforms to CCRGBAProtocol protocol */ +@property (nonatomic,readwrite) GLubyte opacity; +/** RGB colors: conforms to CCRGBAProtocol protocol */ +@property (nonatomic,readwrite) ccColor3B color; +/** whether or not the Sprite is rendered using a CCSpriteBatchNode */ +@property (nonatomic,readwrite) BOOL usesBatchNode; +/** weak reference of the CCTextureAtlas used when the sprite is rendered using a CCSpriteBatchNode */ +@property (nonatomic,readwrite,assign) CCTextureAtlas *textureAtlas; +/** weak reference to the CCSpriteBatchNode that renders the CCSprite */ +@property (nonatomic,readwrite,assign) CCSpriteBatchNode *batchNode; +/** whether or not to transform according to its parent transfomrations. + Useful for health bars. eg: Don't rotate the health bar, even if the parent rotates. + IMPORTANT: Only valid if it is rendered using an CCSpriteBatchNode. + @since v0.99.0 + */ +@property (nonatomic,readwrite) ccHonorParentTransform honorParentTransform; +/** offset position in pixels of the sprite in points. Calculated automatically by editors like Zwoptex. + @since v0.99.0 + */ +@property (nonatomic,readonly) CGPoint offsetPositionInPixels; +/** conforms to CCTextureProtocol protocol */ +@property (nonatomic,readwrite) ccBlendFunc blendFunc; + +#pragma mark CCSprite - Initializers + +/** Creates an sprite with a texture. + The rect used will be the size of the texture. + The offset will be (0,0). + */ ++(id) spriteWithTexture:(CCTexture2D*)texture; + +/** Creates an sprite with a texture and a rect. + The offset will be (0,0). + */ ++(id) spriteWithTexture:(CCTexture2D*)texture rect:(CGRect)rect; + +/** Creates an sprite with an sprite frame. + */ ++(id) spriteWithSpriteFrame:(CCSpriteFrame*)spriteFrame; + +/** Creates an sprite with an sprite frame name. + An CCSpriteFrame will be fetched from the CCSpriteFrameCache by name. + If the CCSpriteFrame doesn't exist it will raise an exception. + @since v0.9 + */ ++(id) spriteWithSpriteFrameName:(NSString*)spriteFrameName; + +/** Creates an sprite with an image filename. + The rect used will be the size of the image. + The offset will be (0,0). + */ ++(id) spriteWithFile:(NSString*)filename; + +/** Creates an sprite with an image filename and a rect. + The offset will be (0,0). + */ ++(id) spriteWithFile:(NSString*)filename rect:(CGRect)rect; + +/** Creates an sprite with a CGImageRef and a key. + The key is used by the CCTextureCache to know if a texture was already created with this CGImage. + For example, a valid key is: @"sprite_frame_01". + If key is nil, then a new texture will be created each time by the CCTextureCache. + @since v0.99.0 + */ ++(id) spriteWithCGImage: (CGImageRef)image key:(NSString*)key; + + +/** Creates an sprite with an CCBatchNode and a rect + */ ++(id) spriteWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect; + + +/** Initializes an sprite with a texture. + The rect used will be the size of the texture. + The offset will be (0,0). + */ +-(id) initWithTexture:(CCTexture2D*)texture; + +/** Initializes an sprite with a texture and a rect in points. + The offset will be (0,0). + */ +-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect; + +/** Initializes an sprite with an sprite frame. + */ +-(id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame; + +/** Initializes an sprite with an sprite frame name. + An CCSpriteFrame will be fetched from the CCSpriteFrameCache by name. + If the CCSpriteFrame doesn't exist it will raise an exception. + @since v0.9 + */ +-(id) initWithSpriteFrameName:(NSString*)spriteFrameName; + +/** Initializes an sprite with an image filename. + The rect used will be the size of the image. + The offset will be (0,0). + */ +-(id) initWithFile:(NSString*)filename; + +/** Initializes an sprite with an image filename, and a rect. + The offset will be (0,0). + */ +-(id) initWithFile:(NSString*)filename rect:(CGRect)rect; + +/** Initializes an sprite with a CGImageRef and a key + The key is used by the CCTextureCache to know if a texture was already created with this CGImage. + For example, a valid key is: @"sprite_frame_01". + If key is nil, then a new texture will be created each time by the CCTextureCache. + @since v0.99.0 + */ +-(id) initWithCGImage:(CGImageRef)image key:(NSString*)key; + +/** Initializes an sprite with an CCSpriteBatchNode and a rect in points + */ +-(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect; + +/** Initializes an sprite with an CCSpriteBatchNode and a rect in pixels + @since v0.99.5 + */ +-(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rectInPixels:(CGRect)rect; + + + +#pragma mark CCSprite - BatchNode methods + +/** updates the quad according the the rotation, position, scale values. + */ +-(void)updateTransform; + +/** updates the texture rect of the CCSprite in points. + */ +-(void) setTextureRect:(CGRect) rect; +/** updates the texture rect, rectRotated and untrimmed size of the CCSprite in pixels + */ +-(void) setTextureRectInPixels:(CGRect)rect rotated:(BOOL)rotated untrimmedSize:(CGSize)size; + +/** tell the sprite to use self-render. + @since v0.99.0 + */ +-(void) useSelfRender; + +/** tell the sprite to use sprite batch node + @since v0.99.0 + */ +-(void) useBatchNode:(CCSpriteBatchNode*)batchNode; + + +#pragma mark CCSprite - Frames + +/** sets a new display frame to the CCSprite. */ +-(void) setDisplayFrame:(CCSpriteFrame*)newFrame; + +/** returns whether or not a CCSpriteFrame is being displayed */ +-(BOOL) isFrameDisplayed:(CCSpriteFrame*)frame; + +/** returns the current displayed frame. */ +-(CCSpriteFrame*) displayedFrame; + +#pragma mark CCSprite - Animation + +/** changes the display frame based on an animation and an index. + @deprecated Will be removed in 1.0.1. Use setDisplayFrameWithAnimationName:index instead + */ +-(void) setDisplayFrame: (NSString*) animationName index:(int) frameIndex DEPRECATED_ATTRIBUTE; + +/** changes the display frame with animation name and index. + The animation name will be get from the CCAnimationCache + @since v0.99.5 + */ +-(void) setDisplayFrameWithAnimationName:(NSString*)animationName index:(int) frameIndex; + +/** returns an Animation given it's name. + + @deprecated Use CCAnimationCache instead. Will be removed in 1.0.1 + */ +-(CCAnimation*)animationByName: (NSString*) animationName DEPRECATED_ATTRIBUTE; + +/** adds an Animation to the Sprite. + + @deprecated Use CCAnimationCache instead. Will be removed in 1.0.1 + */ +-(void) addAnimation: (CCAnimation*) animation DEPRECATED_ATTRIBUTE; + +@end diff --git a/libs/cocos2d/CCSprite.m b/libs/cocos2d/CCSprite.m new file mode 100644 index 0000000..bb781e1 --- /dev/null +++ b/libs/cocos2d/CCSprite.m @@ -0,0 +1,984 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 + +#import "ccConfig.h" +#import "CCSpriteBatchNode.h" +#import "CCSprite.h" +#import "CCSpriteFrame.h" +#import "CCSpriteFrameCache.h" +#import "CCAnimation.h" +#import "CCAnimationCache.h" +#import "CCTextureCache.h" +#import "Support/CGPointExtension.h" +#import "CCDrawingPrimitives.h" + +#pragma mark - +#pragma mark CCSprite + +#if CC_SPRITEBATCHNODE_RENDER_SUBPIXEL +#define RENDER_IN_SUBPIXEL +#else +#define RENDER_IN_SUBPIXEL(__A__) ( (int)(__A__)) +#endif + +// XXX: Optmization +struct transformValues_ { + CGPoint pos; // position x and y + CGPoint scale; // scale x and y + float rotation; + CGPoint ap; // anchor point in pixels + BOOL visible; +}; + +@interface CCSprite (Private) +-(void)updateTextureCoords:(CGRect)rect; +-(void)updateBlendFunc; +-(void) initAnimationDictionary; +-(void) getTransformValues:(struct transformValues_*)tv; // optimization +@end + +@implementation CCSprite + +@synthesize dirty = dirty_; +@synthesize quad = quad_; +@synthesize atlasIndex = atlasIndex_; +@synthesize textureRect = rect_; +@synthesize textureRectRotated = rectRotated_; +@synthesize blendFunc = blendFunc_; +@synthesize usesBatchNode = usesBatchNode_; +@synthesize textureAtlas = textureAtlas_; +@synthesize batchNode = batchNode_; +@synthesize honorParentTransform = honorParentTransform_; +@synthesize offsetPositionInPixels = offsetPositionInPixels_; + + ++(id)spriteWithTexture:(CCTexture2D*)texture +{ + return [[[self alloc] initWithTexture:texture] autorelease]; +} + ++(id)spriteWithTexture:(CCTexture2D*)texture rect:(CGRect)rect +{ + return [[[self alloc] initWithTexture:texture rect:rect] autorelease]; +} + ++(id)spriteWithFile:(NSString*)filename +{ + return [[[self alloc] initWithFile:filename] autorelease]; +} + ++(id)spriteWithFile:(NSString*)filename rect:(CGRect)rect +{ + return [[[self alloc] initWithFile:filename rect:rect] autorelease]; +} + ++(id)spriteWithSpriteFrame:(CCSpriteFrame*)spriteFrame +{ + return [[[self alloc] initWithSpriteFrame:spriteFrame] autorelease]; +} + ++(id)spriteWithSpriteFrameName:(NSString*)spriteFrameName +{ + CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:spriteFrameName]; + return [self spriteWithSpriteFrame:frame]; +} + ++(id)spriteWithCGImage:(CGImageRef)image key:(NSString*)key +{ + return [[[self alloc] initWithCGImage:image key:key] autorelease]; +} + ++(id) spriteWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect +{ + return [[[self alloc] initWithBatchNode:batchNode rect:rect] autorelease]; +} + +-(id) init +{ + if( (self=[super init]) ) { + dirty_ = recursiveDirty_ = NO; + + // by default use "Self Render". + // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render" + [self useSelfRender]; + + opacityModifyRGB_ = YES; + opacity_ = 255; + color_ = colorUnmodified_ = ccWHITE; + + blendFunc_.src = CC_BLEND_SRC; + blendFunc_.dst = CC_BLEND_DST; + + // update texture (calls updateBlendFunc) + [self setTexture:nil]; + + // clean the Quad + bzero(&quad_, sizeof(quad_)); + + flipY_ = flipX_ = NO; + + // lazy alloc + animations_ = nil; + + // default transform anchor: center + anchorPoint_ = ccp(0.5f, 0.5f); + + // zwoptex default values + offsetPositionInPixels_ = CGPointZero; + + honorParentTransform_ = CC_HONOR_PARENT_TRANSFORM_ALL; + hasChildren_ = NO; + + // Atlas: Color + ccColor4B tmpColor = {255,255,255,255}; + quad_.bl.colors = tmpColor; + quad_.br.colors = tmpColor; + quad_.tl.colors = tmpColor; + quad_.tr.colors = tmpColor; + + // Atlas: Vertex + + // updated in "useSelfRender" + + // Atlas: TexCoords + [self setTextureRectInPixels:CGRectZero rotated:NO untrimmedSize:CGSizeZero]; + + // updateMethod selector + updateMethod = (__typeof__(updateMethod))[self methodForSelector:@selector(updateTransform)]; + } + + return self; +} + +-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect +{ + NSAssert(texture!=nil, @"Invalid texture for sprite"); + // IMPORTANT: [self init] and not [super init]; + if( (self = [self init]) ) + { + [self setTexture:texture]; + [self setTextureRect:rect]; + } + return self; +} + +-(id) initWithTexture:(CCTexture2D*)texture +{ + NSAssert(texture!=nil, @"Invalid texture for sprite"); + + CGRect rect = CGRectZero; + rect.size = texture.contentSize; + return [self initWithTexture:texture rect:rect]; +} + +-(id) initWithFile:(NSString*)filename +{ + NSAssert(filename!=nil, @"Invalid filename for sprite"); + + CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage: filename]; + if( texture ) { + CGRect rect = CGRectZero; + rect.size = texture.contentSize; + return [self initWithTexture:texture rect:rect]; + } + + [self release]; + return nil; +} + +-(id) initWithFile:(NSString*)filename rect:(CGRect)rect +{ + NSAssert(filename!=nil, @"Invalid filename for sprite"); + + CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage: filename]; + if( texture ) + return [self initWithTexture:texture rect:rect]; + + [self release]; + return nil; +} + +- (id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame +{ + NSAssert(spriteFrame!=nil, @"Invalid spriteFrame for sprite"); + + id ret = [self initWithTexture:spriteFrame.texture rect:spriteFrame.rect]; + [self setDisplayFrame:spriteFrame]; + return ret; +} + +-(id)initWithSpriteFrameName:(NSString*)spriteFrameName +{ + NSAssert(spriteFrameName!=nil, @"Invalid spriteFrameName for sprite"); + + CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:spriteFrameName]; + return [self initWithSpriteFrame:frame]; +} + +// XXX: deprecated +- (id) initWithCGImage: (CGImageRef)image +{ + NSAssert(image!=nil, @"Invalid CGImageRef for sprite"); + + // XXX: possible bug. See issue #349. New API should be added + NSString *key = [NSString stringWithFormat:@"%08X",(unsigned long)image]; + CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addCGImage:image forKey:key]; + + CGRect rect = CGRectZero; + rect.size = texture.contentSize; + + return [self initWithTexture:texture rect:rect]; +} + +- (id) initWithCGImage:(CGImageRef)image key:(NSString*)key +{ + NSAssert(image!=nil, @"Invalid CGImageRef for sprite"); + + // XXX: possible bug. See issue #349. New API should be added + CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addCGImage:image forKey:key]; + + CGRect rect = CGRectZero; + rect.size = texture.contentSize; + + return [self initWithTexture:texture rect:rect]; +} + +-(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect +{ + id ret = [self initWithTexture:batchNode.texture rect:rect]; + [self useBatchNode:batchNode]; + + return ret; +} + +-(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rectInPixels:(CGRect)rect +{ + id ret = [self initWithTexture:batchNode.texture]; + [self setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size]; + [self useBatchNode:batchNode]; + + return ret; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | Rect = (%.2f,%.2f,%.2f,%.2f) | tag = %i | atlasIndex = %i>", [self class], self, + rect_.origin.x, rect_.origin.y, rect_.size.width, rect_.size.height, + tag_, + atlasIndex_ + ]; +} + +- (void) dealloc +{ + [texture_ release]; + [animations_ release]; + [super dealloc]; +} + +-(void) useSelfRender +{ + atlasIndex_ = CCSpriteIndexNotInitialized; + usesBatchNode_ = NO; + textureAtlas_ = nil; + batchNode_ = nil; + dirty_ = recursiveDirty_ = NO; + + float x1 = 0 + offsetPositionInPixels_.x; + float y1 = 0 + offsetPositionInPixels_.y; + float x2 = x1 + rectInPixels_.size.width; + float y2 = y1 + rectInPixels_.size.height; + quad_.bl.vertices = (ccVertex3F) { x1, y1, 0 }; + quad_.br.vertices = (ccVertex3F) { x2, y1, 0 }; + quad_.tl.vertices = (ccVertex3F) { x1, y2, 0 }; + quad_.tr.vertices = (ccVertex3F) { x2, y2, 0 }; +} + +-(void) useBatchNode:(CCSpriteBatchNode*)batchNode +{ + usesBatchNode_ = YES; + textureAtlas_ = [batchNode textureAtlas]; // weak ref + batchNode_ = batchNode; // weak ref +} + +-(void) initAnimationDictionary +{ + animations_ = [[NSMutableDictionary alloc] initWithCapacity:2]; +} + +-(void)setTextureRect:(CGRect)rect +{ + CGRect rectInPixels = CC_RECT_POINTS_TO_PIXELS( rect ); + [self setTextureRectInPixels:rectInPixels rotated:NO untrimmedSize:rectInPixels.size]; +} + +-(void)setTextureRectInPixels:(CGRect)rect rotated:(BOOL)rotated untrimmedSize:(CGSize)untrimmedSize +{ + rectInPixels_ = rect; + rect_ = CC_RECT_PIXELS_TO_POINTS( rect ); + rectRotated_ = rotated; + + [self setContentSizeInPixels:untrimmedSize]; + [self updateTextureCoords:rectInPixels_]; + + CGPoint relativeOffsetInPixels = unflippedOffsetPositionFromCenter_; + + // issue #732 + if( flipX_ ) + relativeOffsetInPixels.x = -relativeOffsetInPixels.x; + if( flipY_ ) + relativeOffsetInPixels.y = -relativeOffsetInPixels.y; + + offsetPositionInPixels_.x = relativeOffsetInPixels.x + (contentSizeInPixels_.width - rectInPixels_.size.width) / 2; + offsetPositionInPixels_.y = relativeOffsetInPixels.y + (contentSizeInPixels_.height - rectInPixels_.size.height) / 2; + + + // rendering using batch node + if( usesBatchNode_ ) { + // update dirty_, don't update recursiveDirty_ + dirty_ = YES; + } + + // self rendering + else + { + // Atlas: Vertex + float x1 = 0 + offsetPositionInPixels_.x; + float y1 = 0 + offsetPositionInPixels_.y; + float x2 = x1 + rectInPixels_.size.width; + float y2 = y1 + rectInPixels_.size.height; + + // Don't update Z. + quad_.bl.vertices = (ccVertex3F) { x1, y1, 0 }; + quad_.br.vertices = (ccVertex3F) { x2, y1, 0 }; + quad_.tl.vertices = (ccVertex3F) { x1, y2, 0 }; + quad_.tr.vertices = (ccVertex3F) { x2, y2, 0 }; + } +} + +-(void)updateTextureCoords:(CGRect)rect +{ + CCTexture2D *tex = (usesBatchNode_)?[textureAtlas_ texture]:texture_; + if(!tex) + return; + + float atlasWidth = (float)tex.pixelsWide; + float atlasHeight = (float)tex.pixelsHigh; + + float left,right,top,bottom; + + if(rectRotated_){ +#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL + left = (2*rect.origin.x+1)/(2*atlasWidth); + right = left+(rect.size.height*2-2)/(2*atlasWidth); + top = (2*rect.origin.y+1)/(2*atlasHeight); + bottom = top+(rect.size.width*2-2)/(2*atlasHeight); +#else + left = rect.origin.x/atlasWidth; + right = left+(rect.size.height/atlasWidth); + top = rect.origin.y/atlasHeight; + bottom = top+(rect.size.width/atlasHeight); +#endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL + + if( flipX_) + CC_SWAP(top,bottom); + if( flipY_) + CC_SWAP(left,right); + + quad_.bl.texCoords.u = left; + quad_.bl.texCoords.v = top; + quad_.br.texCoords.u = left; + quad_.br.texCoords.v = bottom; + quad_.tl.texCoords.u = right; + quad_.tl.texCoords.v = top; + quad_.tr.texCoords.u = right; + quad_.tr.texCoords.v = bottom; + } else { +#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL + left = (2*rect.origin.x+1)/(2*atlasWidth); + right = left + (rect.size.width*2-2)/(2*atlasWidth); + top = (2*rect.origin.y+1)/(2*atlasHeight); + bottom = top + (rect.size.height*2-2)/(2*atlasHeight); +#else + left = rect.origin.x/atlasWidth; + right = left + rect.size.width/atlasWidth; + top = rect.origin.y/atlasHeight; + bottom = top + rect.size.height/atlasHeight; +#endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL + + if( flipX_) + CC_SWAP(left,right); + if( flipY_) + CC_SWAP(top,bottom); + + quad_.bl.texCoords.u = left; + quad_.bl.texCoords.v = bottom; + quad_.br.texCoords.u = right; + quad_.br.texCoords.v = bottom; + quad_.tl.texCoords.u = left; + quad_.tl.texCoords.v = top; + quad_.tr.texCoords.u = right; + quad_.tr.texCoords.v = top; + } +} + +-(void)updateTransform +{ + NSAssert( usesBatchNode_, @"updateTransform is only valid when CCSprite is being renderd using an CCSpriteBatchNode"); + + // optimization. Quick return if not dirty + if( ! dirty_ ) + return; + + CGAffineTransform matrix; + + // Optimization: if it is not visible, then do nothing + if( ! visible_ ) { + quad_.br.vertices = quad_.tl.vertices = quad_.tr.vertices = quad_.bl.vertices = (ccVertex3F){0,0,0}; + [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_]; + dirty_ = recursiveDirty_ = NO; + return ; + } + + + // Optimization: If parent is batchnode, or parent is nil + // build Affine transform manually + if( ! parent_ || parent_ == batchNode_ ) { + + float radians = -CC_DEGREES_TO_RADIANS(rotation_); + float c = cosf(radians); + float s = sinf(radians); + + matrix = CGAffineTransformMake( c * scaleX_, s * scaleX_, + -s * scaleY_, c * scaleY_, + positionInPixels_.x, positionInPixels_.y); + matrix = CGAffineTransformTranslate(matrix, -anchorPointInPixels_.x, -anchorPointInPixels_.y); + + + } else { // parent_ != batchNode_ + + // else do affine transformation according to the HonorParentTransform + + matrix = CGAffineTransformIdentity; + ccHonorParentTransform prevHonor = CC_HONOR_PARENT_TRANSFORM_ALL; + + for (CCNode *p = self ; p && p != batchNode_ ; p = p.parent) { + + // Might happen. Issue #1053 + NSAssert( [p isKindOfClass:[CCSprite class]], @"CCSprite should be a CCSprite subclass. Probably you initialized an sprite with a batchnode, but you didn't add it to the batch node." ); + + struct transformValues_ tv; + [(CCSprite*)p getTransformValues: &tv]; + + // If any of the parents are not visible, then don't draw this node + if( ! tv.visible ) { + quad_.br.vertices = quad_.tl.vertices = quad_.tr.vertices = quad_.bl.vertices = (ccVertex3F){0,0,0}; + [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_]; + dirty_ = recursiveDirty_ = NO; + return; + } + CGAffineTransform newMatrix = CGAffineTransformIdentity; + + // 2nd: Translate, Rotate, Scale + if( prevHonor & CC_HONOR_PARENT_TRANSFORM_TRANSLATE ) + newMatrix = CGAffineTransformTranslate(newMatrix, tv.pos.x, tv.pos.y); + if( prevHonor & CC_HONOR_PARENT_TRANSFORM_ROTATE ) + newMatrix = CGAffineTransformRotate(newMatrix, -CC_DEGREES_TO_RADIANS(tv.rotation)); + if( prevHonor & CC_HONOR_PARENT_TRANSFORM_SCALE ) { + newMatrix = CGAffineTransformScale(newMatrix, tv.scale.x, tv.scale.y); + } + + // 3rd: Translate anchor point + newMatrix = CGAffineTransformTranslate(newMatrix, -tv.ap.x, -tv.ap.y); + + // 4th: Matrix multiplication + matrix = CGAffineTransformConcat( matrix, newMatrix); + + prevHonor = [(CCSprite*)p honorParentTransform]; + } + } + + + // + // calculate the Quad based on the Affine Matrix + // + + CGSize size = rectInPixels_.size; + + float x1 = offsetPositionInPixels_.x; + float y1 = offsetPositionInPixels_.y; + + float x2 = x1 + size.width; + float y2 = y1 + size.height; + float x = matrix.tx; + float y = matrix.ty; + + float cr = matrix.a; + float sr = matrix.b; + float cr2 = matrix.d; + float sr2 = -matrix.c; + float ax = x1 * cr - y1 * sr2 + x; + float ay = x1 * sr + y1 * cr2 + y; + + float bx = x2 * cr - y1 * sr2 + x; + float by = x2 * sr + y1 * cr2 + y; + + float cx = x2 * cr - y2 * sr2 + x; + float cy = x2 * sr + y2 * cr2 + y; + + float dx = x1 * cr - y2 * sr2 + x; + float dy = x1 * sr + y2 * cr2 + y; + + quad_.bl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(ax), RENDER_IN_SUBPIXEL(ay), vertexZ_ }; + quad_.br.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(bx), RENDER_IN_SUBPIXEL(by), vertexZ_ }; + quad_.tl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(dx), RENDER_IN_SUBPIXEL(dy), vertexZ_ }; + quad_.tr.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(cx), RENDER_IN_SUBPIXEL(cy), vertexZ_ }; + + [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_]; + dirty_ = recursiveDirty_ = NO; +} + +// XXX: Optimization: instead of calling 5 times the parent sprite to obtain: position, scale.x, scale.y, anchorpoint and rotation, +// this fuction return the 5 values in 1 single call +-(void) getTransformValues:(struct transformValues_*) tv +{ + tv->pos = positionInPixels_; + tv->scale.x = scaleX_; + tv->scale.y = scaleY_; + tv->rotation = rotation_; + tv->ap = anchorPointInPixels_; + tv->visible = visible_; +} + +#pragma mark CCSprite - draw + +-(void) draw +{ + NSAssert(!usesBatchNode_, @"If CCSprite is being rendered by CCSpriteBatchNode, CCSprite#draw SHOULD NOT be called"); + + // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Unneeded states: - + + BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST; + if( newBlend ) + glBlendFunc( blendFunc_.src, blendFunc_.dst ); + +#define kQuadSize sizeof(quad_.bl) + glBindTexture(GL_TEXTURE_2D, [texture_ name]); + + long offset = (long)&quad_; + + // vertex + NSInteger diff = offsetof( ccV3F_C4B_T2F, vertices); + glVertexPointer(3, GL_FLOAT, kQuadSize, (void*) (offset + diff) ); + + // color + diff = offsetof( ccV3F_C4B_T2F, colors); + glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (void*)(offset + diff)); + + // tex coords + diff = offsetof( ccV3F_C4B_T2F, texCoords); + glTexCoordPointer(2, GL_FLOAT, kQuadSize, (void*)(offset + diff)); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + if( newBlend ) + glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + +#if CC_SPRITE_DEBUG_DRAW + CGSize s = [self contentSize]; + CGPoint vertices[4]={ + ccp(0,0),ccp(s.width,0), + ccp(s.width,s.height),ccp(0,s.height), + }; + ccDrawPoly(vertices, 4, YES); +#endif // CC_TEXTURENODE_DEBUG_DRAW + +} + +#pragma mark CCSprite - CCNode overrides + +-(void) addChild:(CCSprite*)child z:(NSInteger)z tag:(NSInteger) aTag +{ + NSAssert( child != nil, @"Argument must be non-nil"); + + [super addChild:child z:z tag:aTag]; + + if( usesBatchNode_ ) { + NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSprite only supports CCSprites as children when using CCSpriteBatchNode"); + NSAssert( child.texture.name == textureAtlas_.texture.name, @"CCSprite is not using the same texture id"); + + NSUInteger index = [batchNode_ atlasIndexForChild:child atZ:z]; + [batchNode_ insertChild:child inAtlasAtIndex:index]; + } + + hasChildren_ = YES; +} + +-(void) reorderChild:(CCSprite*)child z:(NSInteger)z +{ + NSAssert( child != nil, @"Child must be non-nil"); + NSAssert( [children_ containsObject:child], @"Child doesn't belong to Sprite" ); + + if( z == child.zOrder ) + return; + + if( usesBatchNode_ ) { + // XXX: Instead of removing/adding, it is more efficient to reorder manually + [child retain]; + [self removeChild:child cleanup:NO]; + [self addChild:child z:z]; + [child release]; + } + + else + [super reorderChild:child z:z]; +} + +-(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup +{ + if( usesBatchNode_ ) + [batchNode_ removeSpriteFromAtlas:sprite]; + + [super removeChild:sprite cleanup:doCleanup]; + + hasChildren_ = ( [children_ count] > 0 ); +} + +-(void)removeAllChildrenWithCleanup:(BOOL)doCleanup +{ + if( usesBatchNode_ ) { + CCSprite *child; + CCARRAY_FOREACH(children_, child) + [batchNode_ removeSpriteFromAtlas:child]; + } + + [super removeAllChildrenWithCleanup:doCleanup]; + + hasChildren_ = NO; +} + +// +// CCNode property overloads +// used only when parent is CCSpriteBatchNode +// +#pragma mark CCSprite - property overloads + + +-(void) setDirtyRecursively:(BOOL)b +{ + dirty_ = recursiveDirty_ = b; + // recursively set dirty + if( hasChildren_ ) { + CCSprite *child; + CCARRAY_FOREACH(children_, child) + [child setDirtyRecursively:YES]; + } +} + +// XXX HACK: optimization +#define SET_DIRTY_RECURSIVELY() { \ + if( usesBatchNode_ && ! recursiveDirty_ ) { \ + dirty_ = recursiveDirty_ = YES; \ + if( hasChildren_) \ + [self setDirtyRecursively:YES]; \ + } \ + } + +-(void)setPosition:(CGPoint)pos +{ + [super setPosition:pos]; + SET_DIRTY_RECURSIVELY(); +} + +-(void)setPositionInPixels:(CGPoint)pos +{ + [super setPositionInPixels:pos]; + SET_DIRTY_RECURSIVELY(); +} + +-(void)setRotation:(float)rot +{ + [super setRotation:rot]; + SET_DIRTY_RECURSIVELY(); +} + +-(void)setScaleX:(float) sx +{ + [super setScaleX:sx]; + SET_DIRTY_RECURSIVELY(); +} + +-(void)setScaleY:(float) sy +{ + [super setScaleY:sy]; + SET_DIRTY_RECURSIVELY(); +} + +-(void)setScale:(float) s +{ + [super setScale:s]; + SET_DIRTY_RECURSIVELY(); +} + +-(void) setVertexZ:(float)z +{ + [super setVertexZ:z]; + SET_DIRTY_RECURSIVELY(); +} + +-(void)setAnchorPoint:(CGPoint)anchor +{ + [super setAnchorPoint:anchor]; + SET_DIRTY_RECURSIVELY(); +} + +-(void)setIsRelativeAnchorPoint:(BOOL)relative +{ + NSAssert( ! usesBatchNode_, @"relativeTransformAnchor is invalid in CCSprite"); + [super setIsRelativeAnchorPoint:relative]; +} + +-(void)setVisible:(BOOL)v +{ + [super setVisible:v]; + SET_DIRTY_RECURSIVELY(); +} + +-(void)setFlipX:(BOOL)b +{ + if( flipX_ != b ) { + flipX_ = b; + [self setTextureRectInPixels:rectInPixels_ rotated:rectRotated_ untrimmedSize:contentSizeInPixels_]; + } +} +-(BOOL) flipX +{ + return flipX_; +} + +-(void) setFlipY:(BOOL)b +{ + if( flipY_ != b ) { + flipY_ = b; + [self setTextureRectInPixels:rectInPixels_ rotated:rectRotated_ untrimmedSize:contentSizeInPixels_]; + } +} +-(BOOL) flipY +{ + return flipY_; +} + +// +// RGBA protocol +// +#pragma mark CCSprite - RGBA protocol +-(void) updateColor +{ + ccColor4B color4 = {color_.r, color_.g, color_.b, opacity_ }; + + quad_.bl.colors = color4; + quad_.br.colors = color4; + quad_.tl.colors = color4; + quad_.tr.colors = color4; + + // renders using Sprite Manager + if( usesBatchNode_ ) { + if( atlasIndex_ != CCSpriteIndexNotInitialized) + [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_]; + else + // no need to set it recursively + // update dirty_, don't update recursiveDirty_ + dirty_ = YES; + } + // self render + // do nothing +} + +-(GLubyte) opacity +{ + return opacity_; +} + +-(void) setOpacity:(GLubyte) anOpacity +{ + opacity_ = anOpacity; + + // special opacity for premultiplied textures + if( opacityModifyRGB_ ) + [self setColor: colorUnmodified_]; + + [self updateColor]; +} + +- (ccColor3B) color +{ + if(opacityModifyRGB_) + return colorUnmodified_; + + return color_; +} + +-(void) setColor:(ccColor3B)color3 +{ + color_ = colorUnmodified_ = color3; + + if( opacityModifyRGB_ ){ + color_.r = color3.r * opacity_/255; + color_.g = color3.g * opacity_/255; + color_.b = color3.b * opacity_/255; + } + + [self updateColor]; +} + +-(void) setOpacityModifyRGB:(BOOL)modify +{ + ccColor3B oldColor = self.color; + opacityModifyRGB_ = modify; + self.color = oldColor; +} + +-(BOOL) doesOpacityModifyRGB +{ + return opacityModifyRGB_; +} + +// +// Frames +// +#pragma mark CCSprite - Frames + +-(void) setDisplayFrame:(CCSpriteFrame*)frame +{ + unflippedOffsetPositionFromCenter_ = frame.offsetInPixels; + + CCTexture2D *newTexture = [frame texture]; + // update texture before updating texture rect + if ( newTexture.name != texture_.name ) + [self setTexture: newTexture]; + + // update rect + rectRotated_ = frame.rotated; + [self setTextureRectInPixels:frame.rectInPixels rotated:frame.rotated untrimmedSize:frame.originalSizeInPixels]; +} + +// XXX deprecated +-(void) setDisplayFrame: (NSString*) animationName index:(int) frameIndex +{ + if( ! animations_ ) + [self initAnimationDictionary]; + + CCAnimation *a = [animations_ objectForKey: animationName]; + CCSpriteFrame *frame = [[a frames] objectAtIndex:frameIndex]; + + NSAssert( frame, @"CCSprite#setDisplayFrame. Invalid frame"); + + [self setDisplayFrame:frame]; +} + +-(void) setDisplayFrameWithAnimationName: (NSString*) animationName index:(int) frameIndex +{ + NSAssert( animationName, @"CCSprite#setDisplayFrameWithAnimationName. animationName must not be nil"); + + CCAnimation *a = [[CCAnimationCache sharedAnimationCache] animationByName:animationName]; + + NSAssert( a, @"CCSprite#setDisplayFrameWithAnimationName: Frame not found"); + + CCSpriteFrame *frame = [[a frames] objectAtIndex:frameIndex]; + + NSAssert( frame, @"CCSprite#setDisplayFrame. Invalid frame"); + + [self setDisplayFrame:frame]; +} + + +-(BOOL) isFrameDisplayed:(CCSpriteFrame*)frame +{ + CGRect r = [frame rect]; + return ( CGRectEqualToRect(r, rect_) && + frame.texture.name == self.texture.name ); +} + +-(CCSpriteFrame*) displayedFrame +{ + return [CCSpriteFrame frameWithTexture:self.texture rect:rect_]; +} + +-(void) addAnimation: (CCAnimation*) anim +{ + // lazy alloc + if( ! animations_ ) + [self initAnimationDictionary]; + + [animations_ setObject:anim forKey:[anim name]]; +} + +-(CCAnimation*)animationByName: (NSString*) animationName +{ + NSAssert( animationName != nil, @"animationName parameter must be non nil"); + return [animations_ objectForKey:animationName]; +} + +#pragma mark CCSprite - CocosNodeTexture protocol + +-(void) updateBlendFunc +{ + NSAssert( ! usesBatchNode_, @"CCSprite: updateBlendFunc doesn't work when the sprite is rendered using a CCSpriteBatchNode"); + + // it's possible to have an untextured sprite + if( !texture_ || ! [texture_ hasPremultipliedAlpha] ) { + blendFunc_.src = GL_SRC_ALPHA; + blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; + [self setOpacityModifyRGB:NO]; + } else { + blendFunc_.src = CC_BLEND_SRC; + blendFunc_.dst = CC_BLEND_DST; + [self setOpacityModifyRGB:YES]; + } +} + +-(void) setTexture:(CCTexture2D*)texture +{ + NSAssert( ! usesBatchNode_, @"CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteBatchNode"); + + // accept texture==nil as argument + NSAssert( !texture || [texture isKindOfClass:[CCTexture2D class]], @"setTexture expects a CCTexture2D. Invalid argument"); + + [texture_ release]; + texture_ = [texture retain]; + + [self updateBlendFunc]; +} + +-(CCTexture2D*) texture +{ + return texture_; +} + +@end diff --git a/libs/cocos2d/CCSpriteBatchNode.h b/libs/cocos2d/CCSpriteBatchNode.h new file mode 100644 index 0000000..49b43b5 --- /dev/null +++ b/libs/cocos2d/CCSpriteBatchNode.h @@ -0,0 +1,143 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009-2010 Ricardo Quesada + * Copyright (C) 2009 Matt Oswald + * + * 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 "CCProtocols.h" +#import "CCTextureAtlas.h" +#import "ccMacros.h" + +#pragma mark CCSpriteBatchNode + +@class CCSprite; + +/** CCSpriteBatchNode is like a batch node: if it contains children, it will draw them in 1 single OpenGL call + * (often known as "batch draw"). + * + * A CCSpriteBatchNode can reference one and only one texture (one image file, one texture atlas). + * Only the CCSprites that are contained in that texture can be added to the CCSpriteBatchNode. + * All CCSprites added to a CCSpriteBatchNode are drawn in one OpenGL ES draw call. + * If the CCSprites are not added to a CCSpriteBatchNode then an OpenGL ES draw call will be needed for each one, which is less efficient. + * + * + * Limitations: + * - The only object that is accepted as child (or grandchild, grand-grandchild, etc...) is CCSprite or any subclass of CCSprite. eg: particles, labels and layer can't be added to a CCSpriteBatchNode. + * - Either all its children are Aliased or Antialiased. It can't be a mix. This is because "alias" is a property of the texture, and all the sprites share the same texture. + * + * @since v0.7.1 + */ +@interface CCSpriteBatchNode : CCNode +{ + CCTextureAtlas *textureAtlas_; + ccBlendFunc blendFunc_; + + // all descendants: chlidren, gran children, etc... + CCArray *descendants_; +} + +/** returns the TextureAtlas that is used */ +@property (nonatomic,readwrite,retain) CCTextureAtlas * textureAtlas; + +/** conforms to CCTextureProtocol protocol */ +@property (nonatomic,readwrite) ccBlendFunc blendFunc; + +/** descendants (children, gran children, etc) */ +@property (nonatomic,readonly) CCArray *descendants; + +/** creates a CCSpriteBatchNode with a texture2d and a default capacity of 29 children. + The capacity will be increased in 33% in runtime if it run out of space. + */ ++(id)batchNodeWithTexture:(CCTexture2D *)tex; ++(id)spriteSheetWithTexture:(CCTexture2D *)tex DEPRECATED_ATTRIBUTE; + +/** creates a CCSpriteBatchNode with a texture2d and capacity of children. + The capacity will be increased in 33% in runtime if it run out of space. + */ ++(id)batchNodeWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity; ++(id)spriteSheetWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity DEPRECATED_ATTRIBUTE; + +/** creates a CCSpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) with a default capacity of 29 children. + The capacity will be increased in 33% in runtime if it run out of space. + The file will be loaded using the TextureMgr. + */ ++(id)batchNodeWithFile:(NSString*) fileImage; ++(id)spriteSheetWithFile:(NSString*) fileImage DEPRECATED_ATTRIBUTE; + +/** creates a CCSpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) and capacity of children. + The capacity will be increased in 33% in runtime if it run out of space. + The file will be loaded using the TextureMgr. +*/ ++(id)batchNodeWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity; ++(id)spriteSheetWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity DEPRECATED_ATTRIBUTE; + +/** initializes a CCSpriteBatchNode with a texture2d and capacity of children. + The capacity will be increased in 33% in runtime if it run out of space. + */ +-(id)initWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity; +/** initializes a CCSpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) and a capacity of children. + The capacity will be increased in 33% in runtime if it run out of space. + The file will be loaded using the TextureMgr. + */ +-(id)initWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity; + +-(void) increaseAtlasCapacity; + +/** creates an sprite with a rect in the CCSpriteBatchNode. + It's the same as: + - create an standard CCSsprite + - set the usingSpriteSheet = YES + - set the textureAtlas to the same texture Atlas as the CCSpriteBatchNode + @deprecated Use [CCSprite spriteWithBatchNode:rect:] instead; + */ +-(CCSprite*) createSpriteWithRect:(CGRect)rect DEPRECATED_ATTRIBUTE; + +/** initializes a previously created sprite with a rect. This sprite will have the same texture as the CCSpriteBatchNode. + It's the same as: + - initialize an standard CCSsprite + - set the usingBatchNode = YES + - set the textureAtlas to the same texture Atlas as the CCSpriteBatchNode + @since v0.99.0 + @deprecated Use [CCSprite initWithBatchNode:rect:] instead; +*/ +-(void) initSprite:(CCSprite*)sprite rect:(CGRect)rect DEPRECATED_ATTRIBUTE; + +/** removes a child given a certain index. It will also cleanup the running actions depending on the cleanup parameter. + @warning Removing a child from a CCSpriteBatchNode is very slow + */ +-(void)removeChildAtIndex:(NSUInteger)index cleanup:(BOOL)doCleanup; + +/** removes a child given a reference. It will also cleanup the running actions depending on the cleanup parameter. + @warning Removing a child from a CCSpriteBatchNode is very slow + */ +-(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup; + +-(void) insertChild:(CCSprite*)child inAtlasAtIndex:(NSUInteger)index; +-(void) removeSpriteFromAtlas:(CCSprite*)sprite; + +-(NSUInteger) rebuildIndexInOrder:(CCSprite*)parent atlasIndex:(NSUInteger)index; +-(NSUInteger) atlasIndexForChild:(CCSprite*)sprite atZ:(NSInteger)z; + +@end diff --git a/libs/cocos2d/CCSpriteBatchNode.m b/libs/cocos2d/CCSpriteBatchNode.m new file mode 100644 index 0000000..0c89680 --- /dev/null +++ b/libs/cocos2d/CCSpriteBatchNode.m @@ -0,0 +1,499 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009-2010 Ricardo Quesada + * Copyright (C) 2009 Matt Oswald + * + * 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 "ccConfig.h" +#import "CCSprite.h" +#import "CCSpriteBatchNode.h" +#import "CCGrid.h" +#import "CCDrawingPrimitives.h" +#import "CCTextureCache.h" +#import "Support/CGPointExtension.h" + +const NSUInteger defaultCapacity = 29; + +#pragma mark - +#pragma mark CCSpriteBatchNode + +static SEL selUpdate = NULL; + +@interface CCSpriteBatchNode (private) +-(void) updateBlendFunc; +@end + +@implementation CCSpriteBatchNode + +@synthesize textureAtlas = textureAtlas_; +@synthesize blendFunc = blendFunc_; +@synthesize descendants = descendants_; + + ++(void) initialize +{ + if ( self == [CCSpriteBatchNode class] ) { + selUpdate = @selector(updateTransform); + } +} +/* + * creation with CCTexture2D + */ ++(id)batchNodeWithTexture:(CCTexture2D *)tex +{ + return [[[self alloc] initWithTexture:tex capacity:defaultCapacity] autorelease]; +} ++(id)spriteSheetWithTexture:(CCTexture2D *)tex // XXX DEPRECATED +{ + return [self batchNodeWithTexture:tex]; +} + ++(id)batchNodeWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity +{ + return [[[self alloc] initWithTexture:tex capacity:capacity] autorelease]; +} ++(id)spriteSheetWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity // XXX DEPRECATED +{ + return [self batchNodeWithTexture:tex capacity:capacity]; +} + +/* + * creation with File Image + */ ++(id)batchNodeWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity +{ + return [[[self alloc] initWithFile:fileImage capacity:capacity] autorelease]; +} ++(id)spriteSheetWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity // XXX DEPRECATED +{ + return [self batchNodeWithFile:fileImage capacity:capacity]; +} + ++(id)batchNodeWithFile:(NSString*) imageFile +{ + return [[[self alloc] initWithFile:imageFile capacity:defaultCapacity] autorelease]; +} ++(id)spriteSheetWithFile:(NSString*) imageFile // XXX DEPRECATED +{ + return [self batchNodeWithFile:imageFile]; +} + + +/* + * init with CCTexture2D + */ +-(id)initWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity +{ + if( (self=[super init])) { + + blendFunc_.src = CC_BLEND_SRC; + blendFunc_.dst = CC_BLEND_DST; + textureAtlas_ = [[CCTextureAtlas alloc] initWithTexture:tex capacity:capacity]; + + [self updateBlendFunc]; + + // no lazy alloc in this node + children_ = [[CCArray alloc] initWithCapacity:capacity]; + descendants_ = [[CCArray alloc] initWithCapacity:capacity]; + } + + return self; +} + +/* + * init with FileImage + */ +-(id)initWithFile:(NSString *)fileImage capacity:(NSUInteger)capacity +{ + CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:fileImage]; + return [self initWithTexture:tex capacity:capacity]; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag_ ]; +} + +-(void)dealloc +{ + [textureAtlas_ release]; + [descendants_ release]; + + [super dealloc]; +} + +#pragma mark CCSpriteBatchNode - composition + +// override visit. +// Don't call visit on it's children +-(void) visit +{ + + // CAREFUL: + // This visit is almost identical to CocosNode#visit + // with the exception that it doesn't call visit on it's children + // + // The alternative is to have a void CCSprite#visit, but + // although this is less mantainable, is faster + // + if (!visible_) + return; + + glPushMatrix(); + + if ( grid_ && grid_.active) { + [grid_ beforeDraw]; + [self transformAncestors]; + } + + [self transform]; + + [self draw]; + + if ( grid_ && grid_.active) + [grid_ afterDraw:self]; + + glPopMatrix(); +} + +// XXX deprecated +-(CCSprite*) createSpriteWithRect:(CGRect)rect +{ + CCSprite *sprite = [CCSprite spriteWithTexture:textureAtlas_.texture rect:rect]; + [sprite useBatchNode:self]; + + return sprite; +} + +// XXX deprecated +-(void) initSprite:(CCSprite*)sprite rect:(CGRect)rect +{ + [sprite initWithTexture:textureAtlas_.texture rect:rect]; + [sprite useBatchNode:self]; +} + +// override addChild: +-(void) addChild:(CCSprite*)child z:(NSInteger)z tag:(NSInteger) aTag +{ + NSAssert( child != nil, @"Argument must be non-nil"); + NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSpriteBatchNode only supports CCSprites as children"); + NSAssert( child.texture.name == textureAtlas_.texture.name, @"CCSprite is not using the same texture id"); + + [super addChild:child z:z tag:aTag]; + + NSUInteger index = [self atlasIndexForChild:child atZ:z]; + [self insertChild:child inAtlasAtIndex:index]; +} + +// override reorderChild +-(void) reorderChild:(CCSprite*)child z:(NSInteger)z +{ + NSAssert( child != nil, @"Child must be non-nil"); + NSAssert( [children_ containsObject:child], @"Child doesn't belong to Sprite" ); + + if( z == child.zOrder ) + return; + + // XXX: Instead of removing/adding, it is more efficient to reorder manually + [child retain]; + [self removeChild:child cleanup:NO]; + [self addChild:child z:z]; + [child release]; +} + +// override removeChild: +-(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup +{ + // explicit nil handling + if (sprite == nil) + return; + + NSAssert([children_ containsObject:sprite], @"CCSpriteBatchNode doesn't contain the sprite. Can't remove it"); + + // cleanup before removing + [self removeSpriteFromAtlas:sprite]; + + [super removeChild:sprite cleanup:doCleanup]; +} + +-(void)removeChildAtIndex:(NSUInteger)index cleanup:(BOOL)doCleanup +{ + [self removeChild:(CCSprite *)[children_ objectAtIndex:index] cleanup:doCleanup]; +} + +-(void)removeAllChildrenWithCleanup:(BOOL)doCleanup +{ + // Invalidate atlas index. issue #569 + [children_ makeObjectsPerformSelector:@selector(useSelfRender)]; + + [super removeAllChildrenWithCleanup:doCleanup]; + + [descendants_ removeAllObjects]; + [textureAtlas_ removeAllQuads]; +} + +#pragma mark CCSpriteBatchNode - draw +-(void) draw +{ + // Optimization: Fast Dispatch + if( textureAtlas_.totalQuads == 0 ) + return; + + CCSprite *child; + ccArray *array = descendants_->data; + + NSUInteger i = array->num; + id *arr = array->arr; + + if( i > 0 ) { + + while (i-- > 0) { + child = *arr++; + + // fast dispatch + child->updateMethod(child, selUpdate); + +#if CC_SPRITEBATCHNODE_DEBUG_DRAW + //Issue #528 + CGRect rect = [child boundingBox]; + CGPoint vertices[4]={ + ccp(rect.origin.x,rect.origin.y), + ccp(rect.origin.x+rect.size.width,rect.origin.y), + ccp(rect.origin.x+rect.size.width,rect.origin.y+rect.size.height), + ccp(rect.origin.x,rect.origin.y+rect.size.height), + }; + ccDrawPoly(vertices, 4, YES); +#endif // CC_SPRITEBATCHNODE_DEBUG_DRAW + } + } + + // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Unneeded states: - + + BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST; + if( newBlend ) + glBlendFunc( blendFunc_.src, blendFunc_.dst ); + + [textureAtlas_ drawQuads]; + if( newBlend ) + glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); +} + +#pragma mark CCSpriteBatchNode - private +-(void) increaseAtlasCapacity +{ + // if we're going beyond the current TextureAtlas's capacity, + // all the previously initialized sprites will need to redo their texture coords + // this is likely computationally expensive + NSUInteger quantity = (textureAtlas_.capacity + 1) * 4 / 3; + + CCLOG(@"cocos2d: CCSpriteBatchNode: resizing TextureAtlas capacity from [%u] to [%u].", + (unsigned int)textureAtlas_.capacity, + (unsigned int)quantity); + + + if( ! [textureAtlas_ resizeCapacity:quantity] ) { + // serious problems + CCLOG(@"cocos2d: WARNING: Not enough memory to resize the atlas"); + NSAssert(NO,@"XXX: SpriteSheet#increaseAtlasCapacity SHALL handle this assert"); + } +} + + +#pragma mark CCSpriteBatchNode - Atlas Index Stuff + +-(NSUInteger) rebuildIndexInOrder:(CCSprite*)node atlasIndex:(NSUInteger)index +{ + CCSprite *sprite; + CCARRAY_FOREACH(node.children, sprite){ + if( sprite.zOrder < 0 ) + index = [self rebuildIndexInOrder:sprite atlasIndex:index]; + } + + // ignore self (batch node) + if( ! [node isEqual:self]) { + node.atlasIndex = index; + index++; + } + + CCARRAY_FOREACH(node.children, sprite){ + if( sprite.zOrder >= 0 ) + index = [self rebuildIndexInOrder:sprite atlasIndex:index]; + } + + return index; +} + +-(NSUInteger) highestAtlasIndexInChild:(CCSprite*)sprite +{ + CCArray *array = [sprite children]; + NSUInteger count = [array count]; + if( count == 0 ) + return sprite.atlasIndex; + else + return [self highestAtlasIndexInChild:[array lastObject]]; +} + +-(NSUInteger) lowestAtlasIndexInChild:(CCSprite*)sprite +{ + CCArray *array = [sprite children]; + NSUInteger count = [array count]; + if( count == 0 ) + return sprite.atlasIndex; + else + return [self lowestAtlasIndexInChild:[array objectAtIndex:0] ]; +} + + +-(NSUInteger)atlasIndexForChild:(CCSprite*)sprite atZ:(NSInteger)z +{ + CCArray *brothers = [[sprite parent] children]; + NSUInteger childIndex = [brothers indexOfObject:sprite]; + + // ignore parent Z if parent is batchnode + BOOL ignoreParent = ( sprite.parent == self ); + CCSprite *previous = nil; + if( childIndex > 0 ) + previous = [brothers objectAtIndex:childIndex-1]; + + // first child of the sprite sheet + if( ignoreParent ) { + if( childIndex == 0 ) + return 0; + // else + return [self highestAtlasIndexInChild: previous] + 1; + } + + // parent is a CCSprite, so, it must be taken into account + + // first child of an CCSprite ? + if( childIndex == 0 ) + { + CCSprite *p = (CCSprite*) sprite.parent; + + // less than parent and brothers + if( z < 0 ) + return p.atlasIndex; + else + return p.atlasIndex+1; + + } else { + // previous & sprite belong to the same branch + if( ( previous.zOrder < 0 && z < 0 )|| (previous.zOrder >= 0 && z >= 0) ) + return [self highestAtlasIndexInChild:previous] + 1; + + // else (previous < 0 and sprite >= 0 ) + CCSprite *p = (CCSprite*) sprite.parent; + return p.atlasIndex + 1; + } + + NSAssert( NO, @"Should not happen. Error calculating Z on Batch Node"); + return 0; +} + +#pragma mark CCSpriteBatchNode - add / remove / reorder helper methods +// add child helper +-(void) insertChild:(CCSprite*)sprite inAtlasAtIndex:(NSUInteger)index +{ + [sprite useBatchNode:self]; + [sprite setAtlasIndex:index]; + [sprite setDirty: YES]; + + if(textureAtlas_.totalQuads == textureAtlas_.capacity) + [self increaseAtlasCapacity]; + + ccV3F_C4B_T2F_Quad quad = [sprite quad]; + [textureAtlas_ insertQuad:&quad atIndex:index]; + + ccArray *descendantsData = descendants_->data; + + ccArrayInsertObjectAtIndex(descendantsData, sprite, index); + + // update indices + NSUInteger i = index+1; + CCSprite *child; + for(; inum; i++){ + child = descendantsData->arr[i]; + child.atlasIndex = child.atlasIndex + 1; + } + + // add children recursively + CCARRAY_FOREACH(sprite.children, child){ + NSUInteger idx = [self atlasIndexForChild:child atZ: child.zOrder]; + [self insertChild:child inAtlasAtIndex:idx]; + } +} + +// remove child helper +-(void) removeSpriteFromAtlas:(CCSprite*)sprite +{ + // remove from TextureAtlas + [textureAtlas_ removeQuadAtIndex:sprite.atlasIndex]; + + // Cleanup sprite. It might be reused (issue #569) + [sprite useSelfRender]; + + ccArray *descendantsData = descendants_->data; + NSUInteger index = ccArrayGetIndexOfObject(descendantsData, sprite); + if( index != NSNotFound ) { + ccArrayRemoveObjectAtIndex(descendantsData, index); + + // update all sprites beyond this one + NSUInteger count = descendantsData->num; + + for(; index < count; index++) + { + CCSprite *s = descendantsData->arr[index]; + s.atlasIndex = s.atlasIndex - 1; + } + } + + // remove children recursively + CCSprite *child; + CCARRAY_FOREACH(sprite.children, child) + [self removeSpriteFromAtlas:child]; +} + +#pragma mark CCSpriteBatchNode - CocosNodeTexture protocol + +-(void) updateBlendFunc +{ + if( ! [textureAtlas_.texture hasPremultipliedAlpha] ) { + blendFunc_.src = GL_SRC_ALPHA; + blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; + } +} + +-(void) setTexture:(CCTexture2D*)texture +{ + textureAtlas_.texture = texture; + [self updateBlendFunc]; +} + +-(CCTexture2D*) texture +{ + return textureAtlas_.texture; +} +@end diff --git a/libs/cocos2d/CCSpriteFrame.h b/libs/cocos2d/CCSpriteFrame.h new file mode 100644 index 0000000..7c59685 --- /dev/null +++ b/libs/cocos2d/CCSpriteFrame.h @@ -0,0 +1,89 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2011 Ricardo Quesada + * + * 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 +#import "CCNode.h" +#import "CCProtocols.h" + +/** A CCSpriteFrame has: + - texture: A CCTexture2D that will be used by the CCSprite + - rectangle: A rectangle of the texture + + + You can modify the frame of a CCSprite by doing: + + CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:texture rect:rect offset:offset]; + [sprite setDisplayFrame:frame]; + */ +@interface CCSpriteFrame : NSObject +{ + CGRect rect_; + CGRect rectInPixels_; + BOOL rotated_; + CGPoint offsetInPixels_; + CGSize originalSizeInPixels_; + CCTexture2D *texture_; +} +/** rect of the frame in points. If it is updated, then rectInPixels will be updated too. */ +@property (nonatomic,readwrite) CGRect rect; + +/** rect of the frame in pixels. If it is updated, then rect (points) will be udpated too. */ +@property (nonatomic,readwrite) CGRect rectInPixels; + +/** whether or not the rect of the frame is rotated ( x = x+width, y = y+height, width = height, height = width ) */ +@property (nonatomic,readwrite) BOOL rotated; + +/** offset of the frame in pixels */ +@property (nonatomic,readwrite) CGPoint offsetInPixels; + +/** original size of the trimmed image in pixels */ +@property (nonatomic,readwrite) CGSize originalSizeInPixels; + +/** texture of the frame */ +@property (nonatomic, retain, readwrite) CCTexture2D *texture; + +/** Create a CCSpriteFrame with a texture, rect in points. + It is assumed that the frame was not trimmed. + */ ++(id) frameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect; + +/** Create a CCSpriteFrame with a texture, rect, rotated, offset and originalSize in pixels. + The originalSize is the size in points of the frame before being trimmed. + */ ++(id) frameWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize; + + +/** Initializes a CCSpriteFrame with a texture, rect in points; + It is assumed that the frame was not trimmed. + */ +-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect; + +/** Initializes a CCSpriteFrame with a texture, rect, rotated, offset and originalSize in pixels. + The originalSize is the size in points of the frame before being trimmed. + */ +-(id) initWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize; + +@end + diff --git a/libs/cocos2d/CCSpriteFrame.m b/libs/cocos2d/CCSpriteFrame.m new file mode 100644 index 0000000..22a525f --- /dev/null +++ b/libs/cocos2d/CCSpriteFrame.m @@ -0,0 +1,108 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2011 Ricardo Quesada + * + * 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 "CCTextureCache.h" +#import "CCSpriteFrame.h" +#import "ccMacros.h" + +@implementation CCSpriteFrame +@synthesize rotated = rotated_, offsetInPixels = offsetInPixels_, texture = texture_; +@synthesize originalSizeInPixels=originalSizeInPixels_; + ++(id) frameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect +{ + return [[[self alloc] initWithTexture:texture rect:rect] autorelease]; +} + ++(id) frameWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize +{ + return [[[self alloc] initWithTexture:texture rectInPixels:rect rotated:rotated offset:offset originalSize:originalSize] autorelease]; +} + +-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect +{ + CGRect rectInPixels = CC_RECT_POINTS_TO_PIXELS( rect ); + return [self initWithTexture:texture rectInPixels:rectInPixels rotated:NO offset:CGPointZero originalSize:rectInPixels.size]; +} + +-(id) initWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize +{ + if( (self=[super init]) ) { + self.texture = texture; + rectInPixels_ = rect; + rect_ = CC_RECT_PIXELS_TO_POINTS( rect ); + rotated_ = rotated; + offsetInPixels_ = offset; + originalSizeInPixels_ = originalSize; + } + return self; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | TextureName=%d, Rect = (%.2f,%.2f,%.2f,%.2f)>", [self class], self, + texture_.name, + rect_.origin.x, + rect_.origin.y, + rect_.size.width, + rect_.size.height]; +} + +- (void) dealloc +{ + CCLOGINFO( @"cocos2d: deallocing %@",self); + [texture_ release]; + [super dealloc]; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCSpriteFrame *copy = [[[self class] allocWithZone: zone] initWithTexture:texture_ rectInPixels:rectInPixels_ rotated:rotated_ offset:offsetInPixels_ originalSize:originalSizeInPixels_]; + return copy; +} + +-(CGRect) rect +{ + return rect_; +} + +-(CGRect) rectInPixels +{ + return rectInPixels_; +} + +-(void) setRect:(CGRect)rect +{ + rect_ = rect; + rectInPixels_ = CC_RECT_POINTS_TO_PIXELS( rect_ ); +} + +-(void) setRectInPixels:(CGRect)rectInPixels +{ + rectInPixels_ = rectInPixels; + rect_ = CC_RECT_PIXELS_TO_POINTS(rectInPixels); +} +@end diff --git a/libs/cocos2d/CCSpriteFrameCache.h b/libs/cocos2d/CCSpriteFrameCache.h new file mode 100644 index 0000000..b50a6a0 --- /dev/null +++ b/libs/cocos2d/CCSpriteFrameCache.h @@ -0,0 +1,133 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2009 Jason Booth + * Copyright (c) 2009 Robert J Payne + * + * 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. + * + */ + + +/* + * To create sprite frames and texture atlas, use this tool: + * http://zwoptex.zwopple.com/ + */ + +#import + +#import "CCSpriteFrame.h" +#import "CCTexture2D.h" + +@class CCSprite; + +/** Singleton that handles the loading of the sprite frames. + It saves in a cache the sprite frames. + @since v0.9 + */ +@interface CCSpriteFrameCache : NSObject +{ + NSMutableDictionary *spriteFrames_; + NSMutableDictionary *spriteFramesAliases_; +} + +/** Retruns ths shared instance of the Sprite Frame cache */ ++ (CCSpriteFrameCache *) sharedSpriteFrameCache; + +/** Purges the cache. It releases all the Sprite Frames and the retained instance. + */ ++(void)purgeSharedSpriteFrameCache; + + +/** Adds multiple Sprite Frames with a dictionary. The texture will be associated with the created sprite frames. + */ +-(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary texture:(CCTexture2D*)texture; + +/** Adds multiple Sprite Frames from a plist file. + * A texture will be loaded automatically. The texture name will composed by replacing the .plist suffix with .png + * If you want to use another texture, you should use the addSpriteFramesWithFile:texture method. + */ +-(void) addSpriteFramesWithFile:(NSString*)plist; + +/** Adds multiple Sprite Frames from a plist file. The texture will be associated with the created sprite frames. + */ +-(void) addSpriteFramesWithFile:(NSString*)plist texture:(CCTexture2D*)texture; + +/** Adds multiple Sprite Frames from a plist file. The texture will be associated with the created sprite frames. + @since v0.99.5 + */ +-(void) addSpriteFramesWithFile:(NSString*)plist textureFile:(NSString*)textureFileName; + +/** Adds an sprite frame with a given name. + If the name already exists, then the contents of the old name will be replaced with the new one. + */ +-(void) addSpriteFrame:(CCSpriteFrame*)frame name:(NSString*)frameName; + + +/** Purges the dictionary of loaded sprite frames. + * Call this method if you receive the "Memory Warning". + * In the short term: it will free some resources preventing your app from being killed. + * In the medium term: it will allocate more resources. + * In the long term: it will be the same. + */ +-(void) removeSpriteFrames; + +/** Removes unused sprite frames. + * Sprite Frames that have a retain count of 1 will be deleted. + * It is convinient to call this method after when starting a new Scene. + */ +-(void) removeUnusedSpriteFrames; + +/** Deletes an sprite frame from the sprite frame cache. + */ +-(void) removeSpriteFrameByName:(NSString*)name; + +/** Removes multiple Sprite Frames from a plist file. +* Sprite Frames stored in this file will be removed. +* It is convinient to call this method when a specific texture needs to be removed. +* @since v0.99.5 +*/ +- (void) removeSpriteFramesFromFile:(NSString*) plist; + +/** Removes multiple Sprite Frames from NSDictionary. + * @since v0.99.5 + */ +- (void) removeSpriteFramesFromDictionary:(NSDictionary*) dictionary; + +/** Removes all Sprite Frames associated with the specified textures. + * It is convinient to call this method when a specific texture needs to be removed. + * @since v0.995. + */ +- (void) removeSpriteFramesFromTexture:(CCTexture2D*) texture; + +/** Returns an Sprite Frame that was previously added. + If the name is not found it will return nil. + You should retain the returned copy if you are going to use it. + */ +-(CCSpriteFrame*) spriteFrameByName:(NSString*)name; + +/** Creates an sprite with the name of an sprite frame. + The created sprite will contain the texture, rect and offset of the sprite frame. + It returns an autorelease object. + @deprecated use [CCSprite spriteWithSpriteFrameName:name]. This method will be removed on final v0.9 + */ +-(CCSprite*) createSpriteWithFrameName:(NSString*)name DEPRECATED_ATTRIBUTE; + +@end diff --git a/libs/cocos2d/CCSpriteFrameCache.m b/libs/cocos2d/CCSpriteFrameCache.m new file mode 100644 index 0000000..514ab97 --- /dev/null +++ b/libs/cocos2d/CCSpriteFrameCache.m @@ -0,0 +1,344 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2009 Jason Booth + * Copyright (c) 2009 Robert J Payne + * + * 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. + * + */ + +/* + * To create sprite frames and texture atlas, use this tool: + * http://zwoptex.zwopple.com/ + */ + +#import "Platforms/CCNS.h" +#import "ccMacros.h" +#import "CCTextureCache.h" +#import "CCSpriteFrameCache.h" +#import "CCSpriteFrame.h" +#import "CCSprite.h" +#import "Support/CCFileUtils.h" + + +@implementation CCSpriteFrameCache + +#pragma mark CCSpriteFrameCache - Alloc, Init & Dealloc + +static CCSpriteFrameCache *sharedSpriteFrameCache_=nil; + ++ (CCSpriteFrameCache *)sharedSpriteFrameCache +{ + if (!sharedSpriteFrameCache_) + sharedSpriteFrameCache_ = [[CCSpriteFrameCache alloc] init]; + + return sharedSpriteFrameCache_; +} + ++(id)alloc +{ + NSAssert(sharedSpriteFrameCache_ == nil, @"Attempted to allocate a second instance of a singleton."); + return [super alloc]; +} + ++(void)purgeSharedSpriteFrameCache +{ + [sharedSpriteFrameCache_ release]; + sharedSpriteFrameCache_ = nil; +} + +-(id) init +{ + if( (self=[super init]) ) { + spriteFrames_ = [[NSMutableDictionary alloc] initWithCapacity: 100]; + spriteFramesAliases_ = [[NSMutableDictionary alloc] initWithCapacity:10]; + } + + return self; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | num of sprite frames = %i>", [self class], self, [spriteFrames_ count]]; +} + +-(void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + + [spriteFrames_ release]; + [spriteFramesAliases_ release]; + [super dealloc]; +} + +#pragma mark CCSpriteFrameCache - loading sprite frames + +-(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary texture:(CCTexture2D*)texture +{ + /* + Supported Zwoptex Formats: + ZWTCoordinatesFormatOptionXMLLegacy = 0, // Flash Version + ZWTCoordinatesFormatOptionXML1_0 = 1, // Desktop Version 0.0 - 0.4b + ZWTCoordinatesFormatOptionXML1_1 = 2, // Desktop Version 1.0.0 - 1.0.1 + ZWTCoordinatesFormatOptionXML1_2 = 3, // Desktop Version 1.0.2+ + */ + NSDictionary *metadataDict = [dictionary objectForKey:@"metadata"]; + NSDictionary *framesDict = [dictionary objectForKey:@"frames"]; + + int format = 0; + + // get the format + if(metadataDict != nil) + format = [[metadataDict objectForKey:@"format"] intValue]; + + // check the format + NSAssert( format >= 0 && format <= 3, @"cocos2d: WARNING: format is not supported for CCSpriteFrameCache addSpriteFramesWithDictionary:texture:"); + + + // add real frames + for(NSString *frameDictKey in framesDict) { + NSDictionary *frameDict = [framesDict objectForKey:frameDictKey]; + CCSpriteFrame *spriteFrame; + if(format == 0) { + float x = [[frameDict objectForKey:@"x"] floatValue]; + float y = [[frameDict objectForKey:@"y"] floatValue]; + float w = [[frameDict objectForKey:@"width"] floatValue]; + float h = [[frameDict objectForKey:@"height"] floatValue]; + float ox = [[frameDict objectForKey:@"offsetX"] floatValue]; + float oy = [[frameDict objectForKey:@"offsetY"] floatValue]; + int ow = [[frameDict objectForKey:@"originalWidth"] intValue]; + int oh = [[frameDict objectForKey:@"originalHeight"] intValue]; + // check ow/oh + if(!ow || !oh) + CCLOG(@"cocos2d: WARNING: originalWidth/Height not found on the CCSpriteFrame. AnchorPoint won't work as expected. Regenerate the .plist"); + + // abs ow/oh + ow = abs(ow); + oh = abs(oh); + // create frame + + spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture + rectInPixels:CGRectMake(x, y, w, h) + rotated:NO + offset:CGPointMake(ox, oy) + originalSize:CGSizeMake(ow, oh)]; + } else if(format == 1 || format == 2) { + CGRect frame = CCRectFromString([frameDict objectForKey:@"frame"]); + BOOL rotated = NO; + + // rotation + if(format == 2) + rotated = [[frameDict objectForKey:@"rotated"] boolValue]; + + CGPoint offset = CCPointFromString([frameDict objectForKey:@"offset"]); + CGSize sourceSize = CCSizeFromString([frameDict objectForKey:@"sourceSize"]); + + // create frame + spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture + rectInPixels:frame + rotated:rotated + offset:offset + originalSize:sourceSize]; + } else if(format == 3) { + // get values + CGSize spriteSize = CCSizeFromString([frameDict objectForKey:@"spriteSize"]); + CGPoint spriteOffset = CCPointFromString([frameDict objectForKey:@"spriteOffset"]); + CGSize spriteSourceSize = CCSizeFromString([frameDict objectForKey:@"spriteSourceSize"]); + CGRect textureRect = CCRectFromString([frameDict objectForKey:@"textureRect"]); + BOOL textureRotated = [[frameDict objectForKey:@"textureRotated"] boolValue]; + + // get aliases + NSArray *aliases = [frameDict objectForKey:@"aliases"]; + for(NSString *alias in aliases) { + if( [spriteFramesAliases_ objectForKey:alias] ) + CCLOG(@"cocos2d: WARNING: an alias with name %@ already exists",alias); + + [spriteFramesAliases_ setObject:frameDictKey forKey:alias]; + } + + // create frame + spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture + rectInPixels:CGRectMake(textureRect.origin.x, textureRect.origin.y, spriteSize.width, spriteSize.height) + rotated:textureRotated + offset:spriteOffset + originalSize:spriteSourceSize]; + } + + // add sprite frame + [spriteFrames_ setObject:spriteFrame forKey:frameDictKey]; + [spriteFrame release]; + } +} + +-(void) addSpriteFramesWithFile:(NSString*)plist texture:(CCTexture2D*)texture +{ + NSString *path = [CCFileUtils fullPathFromRelativePath:plist]; + NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; + + return [self addSpriteFramesWithDictionary:dict texture:texture]; +} + +-(void) addSpriteFramesWithFile:(NSString*)plist textureFile:(NSString*)textureFileName +{ + NSAssert( textureFileName, @"Invalid texture file name"); + CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:textureFileName]; + + if( texture ) + [self addSpriteFramesWithFile:plist texture:texture]; + else + CCLOG(@"cocos2d: CCSpriteFrameCache: couldn't load texture file. File not found: %@", textureFileName); +} + +-(void) addSpriteFramesWithFile:(NSString*)plist +{ + NSString *path = [CCFileUtils fullPathFromRelativePath:plist]; + NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; + + NSString *texturePath = nil; + NSDictionary *metadataDict = [dict objectForKey:@"metadata"]; + if( metadataDict ) + // try to read texture file name from meta data + texturePath = [metadataDict objectForKey:@"textureFileName"]; + + + if( texturePath ) + { + // build texture path relative to plist file + NSString *textureBase = [plist stringByDeletingLastPathComponent]; + texturePath = [textureBase stringByAppendingPathComponent:texturePath]; + } else { + // build texture path by replacing file extension + texturePath = [plist stringByDeletingPathExtension]; + texturePath = [texturePath stringByAppendingPathExtension:@"png"]; + + CCLOG(@"cocos2d: CCSpriteFrameCache: Trying to use file '%@' as texture", texturePath); + } + + CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:texturePath]; + + if( texture ) + [self addSpriteFramesWithDictionary:dict texture:texture]; + + else + CCLOG(@"cocos2d: CCSpriteFrameCache: Couldn't load texture"); +} + +-(void) addSpriteFrame:(CCSpriteFrame*)frame name:(NSString*)frameName +{ + [spriteFrames_ setObject:frame forKey:frameName]; +} + +#pragma mark CCSpriteFrameCache - removing + +-(void) removeSpriteFrames +{ + [spriteFrames_ removeAllObjects]; + [spriteFramesAliases_ removeAllObjects]; +} + +-(void) removeUnusedSpriteFrames +{ + NSArray *keys = [spriteFrames_ allKeys]; + for( id key in keys ) { + id value = [spriteFrames_ objectForKey:key]; + if( [value retainCount] == 1 ) { + CCLOG(@"cocos2d: CCSpriteFrameCache: removing unused frame: %@", key); + [spriteFrames_ removeObjectForKey:key]; + } + } +} + +-(void) removeSpriteFrameByName:(NSString*)name +{ + // explicit nil handling + if( ! name ) + return; + + // Is this an alias ? + NSString *key = [spriteFramesAliases_ objectForKey:name]; + + if( key ) { + [spriteFrames_ removeObjectForKey:key]; + [spriteFramesAliases_ removeObjectForKey:name]; + + } else + [spriteFrames_ removeObjectForKey:name]; +} + +- (void) removeSpriteFramesFromFile:(NSString*) plist +{ + NSString *path = [CCFileUtils fullPathFromRelativePath:plist]; + NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; + + [self removeSpriteFramesFromDictionary:dict]; +} + +- (void) removeSpriteFramesFromDictionary:(NSDictionary*) dictionary +{ + NSDictionary *framesDict = [dictionary objectForKey:@"frames"]; + NSMutableArray *keysToRemove=[NSMutableArray array]; + + for(NSString *frameDictKey in framesDict) + { + if ([spriteFrames_ objectForKey:frameDictKey]!=nil) + [keysToRemove addObject:frameDictKey]; + } + [spriteFrames_ removeObjectsForKeys:keysToRemove]; +} + +- (void) removeSpriteFramesFromTexture:(CCTexture2D*) texture +{ + NSMutableArray *keysToRemove=[NSMutableArray array]; + + for (NSString *spriteFrameKey in spriteFrames_) + { + if ([[spriteFrames_ valueForKey:spriteFrameKey] texture] == texture) + [keysToRemove addObject:spriteFrameKey]; + + } + [spriteFrames_ removeObjectsForKeys:keysToRemove]; +} + +#pragma mark CCSpriteFrameCache - getting + +-(CCSpriteFrame*) spriteFrameByName:(NSString*)name +{ + CCSpriteFrame *frame = [spriteFrames_ objectForKey:name]; + if( ! frame ) { + // try alias dictionary + NSString *key = [spriteFramesAliases_ objectForKey:name]; + frame = [spriteFrames_ objectForKey:key]; + + if( ! frame ) + CCLOG(@"cocos2d: CCSpriteFrameCache: Frame '%@' not found", name); + } + + return frame; +} + +#pragma mark CCSpriteFrameCache - sprite creation + +-(CCSprite*) createSpriteWithFrameName:(NSString*)name +{ + CCSpriteFrame *frame = [spriteFrames_ objectForKey:name]; + return [CCSprite spriteWithSpriteFrame:frame]; +} +@end diff --git a/libs/cocos2d/CCTMXLayer.h b/libs/cocos2d/CCTMXLayer.h new file mode 100644 index 0000000..ead12e0 --- /dev/null +++ b/libs/cocos2d/CCTMXLayer.h @@ -0,0 +1,151 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009-2010 Ricardo Quesada + * + * 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. + * + * + * TMX Tiled Map support: + * http://www.mapeditor.org + * + */ + + +#import "CCAtlasNode.h" +#import "CCSpriteBatchNode.h" + + +@class CCTMXMapInfo; +@class CCTMXLayerInfo; +@class CCTMXTilesetInfo; + +/** CCTMXLayer represents the TMX layer. + + It is a subclass of CCSpriteSheet. By default the tiles are rendered using a CCTextureAtlas. + If you mofify a tile on runtime, then, that tile will become a CCSprite, otherwise no CCSprite objects are created. + The benefits of using CCSprite objects as tiles are: + - tiles (CCSprite) can be rotated/scaled/moved with a nice API + + If the layer contains a property named "cc_vertexz" with an integer (in can be positive or negative), + then all the tiles belonging to the layer will use that value as their OpenGL vertex Z for depth. + + On the other hand, if the "cc_vertexz" property has the "automatic" value, then the tiles will use an automatic vertex Z value. + Also before drawing the tiles, GL_ALPHA_TEST will be enabled, and disabled after drawing them. The used alpha func will be: + + glAlphaFunc( GL_GREATER, value ) + + "value" by default is 0, but you can change it from Tiled by adding the "cc_alpha_func" property to the layer. + The value 0 should work for most cases, but if you have tiles that are semi-transparent, then you might want to use a differnt + value, like 0.5. + + For further information, please see the programming guide: + + http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:tiled_maps + + @since v0.8.1 + */ +@interface CCTMXLayer : CCSpriteBatchNode +{ + CCTMXTilesetInfo *tileset_; + NSString *layerName_; + CGSize layerSize_; + CGSize mapTileSize_; + uint32_t *tiles_; // GID are 32 bit + NSUInteger layerOrientation_; + NSMutableArray *properties_; + + unsigned char opacity_; // TMX Layer supports opacity + + NSUInteger minGID_; + NSUInteger maxGID_; + + // Only used when vertexZ is used + NSInteger vertexZvalue_; + BOOL useAutomaticVertexZ_; + float alphaFuncValue_; + + // used for optimization + CCSprite *reusedTile_; + ccCArray *atlasIndexArray_; +} +/** name of the layer */ +@property (nonatomic,readwrite,retain) NSString *layerName; +/** size of the layer in tiles */ +@property (nonatomic,readwrite) CGSize layerSize; +/** size of the map's tile (could be differnt from the tile's size) */ +@property (nonatomic,readwrite) CGSize mapTileSize; +/** pointer to the map of tiles */ +@property (nonatomic,readwrite) uint32_t *tiles; +/** Tilset information for the layer */ +@property (nonatomic,readwrite,retain) CCTMXTilesetInfo *tileset; +/** Layer orientation, which is the same as the map orientation */ +@property (nonatomic,readwrite) NSUInteger layerOrientation; +/** properties from the layer. They can be added using Tiled */ +@property (nonatomic,readwrite,retain) NSMutableArray *properties; + +/** creates a CCTMXLayer with an tileset info, a layer info and a map info */ ++(id) layerWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo; +/** initializes a CCTMXLayer with a tileset info, a layer info and a map info */ +-(id) initWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo; + +/** dealloc the map that contains the tile position from memory. + Unless you want to know at runtime the tiles positions, you can safely call this method. + If you are going to call [layer tileGIDAt:] then, don't release the map + */ +-(void) releaseMap; + +/** returns the tile (CCSprite) at a given a tile coordinate. + The returned CCSprite will be already added to the CCTMXLayer. Don't add it again. + The CCSprite can be treated like any other CCSprite: rotated, scaled, translated, opacity, color, etc. + You can remove either by calling: + - [layer removeChild:sprite cleanup:cleanup]; + - or [layer removeTileAt:ccp(x,y)]; + */ +-(CCSprite*) tileAt:(CGPoint)tileCoordinate; + +/** returns the tile gid at a given tile coordinate. + if it returns 0, it means that the tile is empty. + This method requires the the tile map has not been previously released (eg. don't call [layer releaseMap]) + */ +-(uint32_t) tileGIDAt:(CGPoint)tileCoordinate; + +/** sets the tile gid (gid = tile global id) at a given tile coordinate. + The Tile GID can be obtained by using the method "tileGIDAt" or by using the TMX editor -> Tileset Mgr +1. + If a tile is already placed at that position, then it will be removed. + */ +-(void) setTileGID:(uint32_t)gid at:(CGPoint)tileCoordinate; + +/** removes a tile at given tile coordinate */ +-(void) removeTileAt:(CGPoint)tileCoordinate; + +/** returns the position in pixels of a given tile coordinate */ +-(CGPoint) positionAt:(CGPoint)tileCoordinate; + +/** return the value for the specific property name */ +-(id) propertyNamed:(NSString *)propertyName; + +/** Creates the tiles */ +-(void) setupTiles; + +/** CCTMXLayer doesn't support adding a CCSprite manually. + @warning addchild:z:tag: is not supported on CCTMXLayer. Instead of setTileGID:at:/tileAt: + */ +-(void) addChild: (CCNode*)node z:(NSInteger)z tag:(NSInteger)tag; +@end diff --git a/libs/cocos2d/CCTMXLayer.m b/libs/cocos2d/CCTMXLayer.m new file mode 100644 index 0000000..86b258d --- /dev/null +++ b/libs/cocos2d/CCTMXLayer.m @@ -0,0 +1,667 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009-2010 Ricardo Quesada + * + * 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. + * + * + * TMX Tiled Map support: + * http://www.mapeditor.org + * + */ + +#import "CCTMXLayer.h" +#import "CCTMXTiledMap.h" +#import "CCTMXXMLParser.h" +#import "CCSprite.h" +#import "CCSpriteBatchNode.h" +#import "CCTextureCache.h" +#import "Support/CGPointExtension.h" + +#pragma mark - +#pragma mark CCSpriteBatchNode Extension + +/* IMPORTANT XXX IMPORTNAT: + * These 2 methods can't be part of CCTMXLayer since they call [super add...], and CCSpriteSheet#add SHALL not be called + */ +@implementation CCSpriteBatchNode (TMXTiledMapExtension) + +/* Adds a quad into the texture atlas but it won't be added into the children array. + This method should be called only when you are dealing with very big AtlasSrite and when most of the CCSprite won't be updated. + For example: a tile map (CCTMXMap) or a label with lots of characgers (BitmapFontAtlas) + */ +-(void) addQuadFromSprite:(CCSprite*)sprite quadIndex:(NSUInteger)index +{ + NSAssert( sprite != nil, @"Argument must be non-nil"); + NSAssert( [sprite isKindOfClass:[CCSprite class]], @"CCSpriteSheet only supports CCSprites as children"); + + + while(index >= textureAtlas_.capacity || textureAtlas_.capacity == textureAtlas_.totalQuads ) + [self increaseAtlasCapacity]; + + // + // update the quad directly. Don't add the sprite to the scene graph + // + + [sprite useBatchNode:self]; + [sprite setAtlasIndex:index]; + + ccV3F_C4B_T2F_Quad quad = [sprite quad]; + [textureAtlas_ insertQuad:&quad atIndex:index]; + + // XXX: updateTransform will update the textureAtlas too using updateQuad. + // XXX: so, it should be AFTER the insertQuad + [sprite setDirty:YES]; + [sprite updateTransform]; +} + +/* This is the opposite of "addQuadFromSprite. + It add the sprite to the children and descendants array, but it doesn't update add it to the texture atlas + */ +-(id) addSpriteWithoutQuad:(CCSprite*)child z:(NSUInteger)z tag:(NSInteger)aTag +{ + NSAssert( child != nil, @"Argument must be non-nil"); + NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSpriteSheet only supports CCSprites as children"); + + // quad index is Z + [child setAtlasIndex:z]; + + // XXX: optimize with a binary search + int i=0; + for( CCSprite *c in descendants_ ) { + if( c.atlasIndex >= z ) + break; + i++; + } + [descendants_ insertObject:child atIndex:i]; + + + // IMPORTANT: Call super, and not self. Avoid adding it to the texture atlas array + [super addChild:child z:z tag:aTag]; + return self; +} +@end + + +#pragma mark - +#pragma mark CCTMXLayer + +@interface CCTMXLayer (Private) +-(CGPoint) positionForIsoAt:(CGPoint)pos; +-(CGPoint) positionForOrthoAt:(CGPoint)pos; +-(CGPoint) positionForHexAt:(CGPoint)pos; + +-(CGPoint) calculateLayerOffset:(CGPoint)offset; + +/* optimization methos */ +-(CCSprite*) appendTileForGID:(uint32_t)gid at:(CGPoint)pos; +-(CCSprite*) insertTileForGID:(uint32_t)gid at:(CGPoint)pos; +-(CCSprite*) updateTileForGID:(uint32_t)gid at:(CGPoint)pos; + +/* The layer recognizes some special properties, like cc_vertez */ +-(void) parseInternalProperties; + +-(NSInteger) vertexZForPos:(CGPoint)pos; + +// adding quad from sprite +-(void)addQuadFromSprite:(CCSprite*)sprite quadIndex:(NSUInteger)index; + +// adds an sprite without the quad +-(id)addSpriteWithoutQuad:(CCSprite*)child z:(NSInteger)z tag:(NSInteger)aTag; + +// index +-(NSUInteger) atlasIndexForExistantZ:(NSUInteger)z; +-(NSUInteger) atlasIndexForNewZ:(NSUInteger)z; +@end + +@implementation CCTMXLayer +@synthesize layerSize = layerSize_, layerName = layerName_, tiles = tiles_; +@synthesize tileset = tileset_; +@synthesize layerOrientation = layerOrientation_; +@synthesize mapTileSize = mapTileSize_; +@synthesize properties = properties_; + +#pragma mark CCTMXLayer - init & alloc & dealloc + ++(id) layerWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo +{ + return [[[self alloc] initWithTilesetInfo:tilesetInfo layerInfo:layerInfo mapInfo:mapInfo] autorelease]; +} + +-(id) initWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo +{ + // XXX: is 35% a good estimate ? + CGSize size = layerInfo.layerSize; + float totalNumberOfTiles = size.width * size.height; + float capacity = totalNumberOfTiles * 0.35f + 1; // 35 percent is occupied ? + + CCTexture2D *tex = nil; + if( tilesetInfo ) + tex = [[CCTextureCache sharedTextureCache] addImage:tilesetInfo.sourceImage]; + + if((self = [super initWithTexture:tex capacity:capacity])) { + + // layerInfo + self.layerName = layerInfo.name; + layerSize_ = layerInfo.layerSize; + tiles_ = layerInfo.tiles; + minGID_ = layerInfo.minGID; + maxGID_ = layerInfo.maxGID; + opacity_ = layerInfo.opacity; + self.properties = [NSMutableDictionary dictionaryWithDictionary:layerInfo.properties]; + + // tilesetInfo + self.tileset = tilesetInfo; + + // mapInfo + mapTileSize_ = mapInfo.tileSize; + layerOrientation_ = mapInfo.orientation; + + // offset (after layer orientation is set); + CGPoint offset = [self calculateLayerOffset:layerInfo.offset]; + [self setPositionInPixels:offset]; + + atlasIndexArray_ = ccCArrayNew(totalNumberOfTiles); + + [self setContentSizeInPixels: CGSizeMake( layerSize_.width * mapTileSize_.width, layerSize_.height * mapTileSize_.height )]; + + useAutomaticVertexZ_= NO; + vertexZvalue_ = 0; + alphaFuncValue_ = 0; + + } + return self; +} + +- (void) dealloc +{ + [layerName_ release]; + [tileset_ release]; + [reusedTile_ release]; + [properties_ release]; + + if( atlasIndexArray_ ) { + ccCArrayFree(atlasIndexArray_); + atlasIndexArray_ = NULL; + } + + if( tiles_ ) { + free(tiles_); + tiles_ = NULL; + } + + [super dealloc]; +} + +-(void) releaseMap +{ + if( tiles_) { + free( tiles_); + tiles_ = NULL; + } + + if( atlasIndexArray_ ) { + ccCArrayFree(atlasIndexArray_); + atlasIndexArray_ = NULL; + } +} + +#pragma mark CCTMXLayer - setup Tiles + +-(void) setupTiles +{ + // Optimization: quick hack that sets the image size on the tileset + tileset_.imageSize = [textureAtlas_.texture contentSizeInPixels]; + + // By default all the tiles are aliased + // pros: + // - easier to render + // cons: + // - difficult to scale / rotate / etc. + [textureAtlas_.texture setAliasTexParameters]; + + CFByteOrder o = CFByteOrderGetCurrent(); + + // Parse cocos2d properties + [self parseInternalProperties]; + + for( NSUInteger y=0; y < layerSize_.height; y++ ) { + for( NSUInteger x=0; x < layerSize_.width; x++ ) { + + NSUInteger pos = x + layerSize_.width * y; + uint32_t gid = tiles_[ pos ]; + + // gid are stored in little endian. + // if host is big endian, then swap + if( o == CFByteOrderBigEndian ) + gid = CFSwapInt32( gid ); + + // XXX: gid == 0 --> empty tile + if( gid != 0 ) { + [self appendTileForGID:gid at:ccp(x,y)]; + + // Optimization: update min and max GID rendered by the layer + minGID_ = MIN(gid, minGID_); + maxGID_ = MAX(gid, maxGID_); + } + } + } + + NSAssert( maxGID_ >= tileset_.firstGid && + minGID_ >= tileset_.firstGid, @"TMX: Only 1 tilset per layer is supported"); +} + +#pragma mark CCTMXLayer - Properties + +-(id) propertyNamed:(NSString *)propertyName +{ + return [properties_ valueForKey:propertyName]; +} + +-(void) parseInternalProperties +{ + // if cc_vertex=automatic, then tiles will be rendered using vertexz + + NSString *vertexz = [self propertyNamed:@"cc_vertexz"]; + if( vertexz ) { + if( [vertexz isEqualToString:@"automatic"] ) + useAutomaticVertexZ_ = YES; + else + vertexZvalue_ = [vertexz intValue]; + } + + NSString *alphaFuncVal = [self propertyNamed:@"cc_alpha_func"]; + alphaFuncValue_ = [alphaFuncVal floatValue]; +} + +#pragma mark CCTMXLayer - obtaining tiles/gids + +-(CCSprite*) tileAt:(CGPoint)pos +{ + NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position"); + NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released"); + + CCSprite *tile = nil; + uint32_t gid = [self tileGIDAt:pos]; + + // if GID == 0, then no tile is present + if( gid ) { + int z = pos.x + pos.y * layerSize_.width; + tile = (CCSprite*) [self getChildByTag:z]; + + // tile not created yet. create it + if( ! tile ) { + CGRect rect = [tileset_ rectForGID:gid]; + tile = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect]; + [tile setPositionInPixels: [self positionAt:pos]]; + [tile setVertexZ: [self vertexZForPos:pos]]; + tile.anchorPoint = CGPointZero; + [tile setOpacity:opacity_]; + + NSUInteger indexForZ = [self atlasIndexForExistantZ:z]; + [self addSpriteWithoutQuad:tile z:indexForZ tag:z]; + [tile release]; + } + } + return tile; +} + +-(uint32_t) tileGIDAt:(CGPoint)pos +{ + NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position"); + NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released"); + + NSInteger idx = pos.x + pos.y * layerSize_.width; + return tiles_[ idx ]; +} + +#pragma mark CCTMXLayer - adding helper methods + +-(CCSprite*) insertTileForGID:(uint32_t)gid at:(CGPoint)pos +{ + CGRect rect = [tileset_ rectForGID:gid]; + + NSInteger z = pos.x + pos.y * layerSize_.width; + + if( ! reusedTile_ ) + reusedTile_ = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect]; + else + [reusedTile_ initWithBatchNode:self rectInPixels:rect]; + + [reusedTile_ setPositionInPixels: [self positionAt:pos]]; + [reusedTile_ setVertexZ: [self vertexZForPos:pos]]; + reusedTile_.anchorPoint = CGPointZero; + [reusedTile_ setOpacity:opacity_]; + + // get atlas index + NSUInteger indexForZ = [self atlasIndexForNewZ:z]; + + // Optimization: add the quad without adding a child + [self addQuadFromSprite:reusedTile_ quadIndex:indexForZ]; + + // insert it into the local atlasindex array + ccCArrayInsertValueAtIndex(atlasIndexArray_, (void*)z, indexForZ); + + // update possible children + CCSprite *sprite; + CCARRAY_FOREACH(children_, sprite) { + NSUInteger ai = [sprite atlasIndex]; + if( ai >= indexForZ) + [sprite setAtlasIndex: ai+1]; + } + + tiles_[z] = gid; + + return reusedTile_; +} + +-(CCSprite*) updateTileForGID:(uint32_t)gid at:(CGPoint)pos +{ + CGRect rect = [tileset_ rectForGID:gid]; + + int z = pos.x + pos.y * layerSize_.width; + + if( ! reusedTile_ ) + reusedTile_ = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect]; + else + [reusedTile_ initWithBatchNode:self rectInPixels:rect]; + + [reusedTile_ setPositionInPixels: [self positionAt:pos]]; + [reusedTile_ setVertexZ: [self vertexZForPos:pos]]; + reusedTile_.anchorPoint = CGPointZero; + [reusedTile_ setOpacity:opacity_]; + + // get atlas index + NSUInteger indexForZ = [self atlasIndexForExistantZ:z]; + + [reusedTile_ setAtlasIndex:indexForZ]; + [reusedTile_ setDirty:YES]; + [reusedTile_ updateTransform]; + tiles_[z] = gid; + + return reusedTile_; +} + + +// used only when parsing the map. useless after the map was parsed +// since lot's of assumptions are no longer true +-(CCSprite*) appendTileForGID:(uint32_t)gid at:(CGPoint)pos +{ + CGRect rect = [tileset_ rectForGID:gid]; + + NSInteger z = pos.x + pos.y * layerSize_.width; + + if( ! reusedTile_ ) + reusedTile_ = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect]; + else + [reusedTile_ initWithBatchNode:self rectInPixels:rect]; + + [reusedTile_ setPositionInPixels: [self positionAt:pos]]; + [reusedTile_ setVertexZ: [self vertexZForPos:pos]]; + reusedTile_.anchorPoint = CGPointZero; + [reusedTile_ setOpacity:opacity_]; + + // optimization: + // The difference between appendTileForGID and insertTileforGID is that append is faster, since + // it appends the tile at the end of the texture atlas + NSUInteger indexForZ = atlasIndexArray_->num; + + + // don't add it using the "standard" way. + [self addQuadFromSprite:reusedTile_ quadIndex:indexForZ]; + + + // append should be after addQuadFromSprite since it modifies the quantity values + ccCArrayInsertValueAtIndex(atlasIndexArray_, (void*)z, indexForZ); + + return reusedTile_; +} + +#pragma mark CCTMXLayer - atlasIndex and Z + +int compareInts (const void * a, const void * b) +{ + return ( *(int*)a - *(int*)b ); +} + +-(NSUInteger) atlasIndexForExistantZ:(NSUInteger)z +{ + NSInteger key = z; + NSInteger *item = bsearch((void*)&key, (void*)&atlasIndexArray_->arr[0], atlasIndexArray_->num, sizeof(void*), compareInts); + + NSAssert( item, @"TMX atlas index not found. Shall not happen"); + + NSUInteger index = ((NSInteger)item - (NSInteger)atlasIndexArray_->arr) / sizeof(void*); + return index; +} + +-(NSUInteger)atlasIndexForNewZ:(NSUInteger)z +{ + // XXX: This can be improved with a sort of binary search + NSUInteger i = 0; + for(i = 0; i< atlasIndexArray_->num; i++) { + NSUInteger val = (NSUInteger) atlasIndexArray_->arr[i]; + if( z < val ) + break; + } + return i; +} + +#pragma mark CCTMXLayer - adding / remove tiles + +-(void) setTileGID:(uint32_t)gid at:(CGPoint)pos +{ + NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position"); + NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released"); + NSAssert( gid == 0 || gid >= tileset_.firstGid, @"TMXLayer: invalid gid" ); + + uint32_t currentGID = [self tileGIDAt:pos]; + + if( currentGID != gid ) { + + // setting gid=0 is equal to remove the tile + if( gid == 0 ) + [self removeTileAt:pos]; + + // empty tile. create a new one + else if( currentGID == 0 ) + [self insertTileForGID:gid at:pos]; + + // modifying an existing tile with a non-empty tile + else { + + NSUInteger z = pos.x + pos.y * layerSize_.width; + id sprite = [self getChildByTag:z]; + if( sprite ) { + CGRect rect = [tileset_ rectForGID:gid]; + [sprite setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size]; + tiles_[z] = gid; + } else + [self updateTileForGID:gid at:pos]; + } + } +} + +-(void) addChild: (CCNode*)node z:(NSInteger)z tag:(NSInteger)tag +{ + NSAssert(NO, @"addChild: is not supported on CCTMXLayer. Instead use setTileGID:at:/tileAt:"); +} + +-(void) removeChild:(CCSprite*)sprite cleanup:(BOOL)cleanup +{ + // allows removing nil objects + if( ! sprite ) + return; + + NSAssert( [children_ containsObject:sprite], @"Tile does not belong to TMXLayer"); + + NSUInteger atlasIndex = [sprite atlasIndex]; + NSUInteger zz = (NSUInteger) atlasIndexArray_->arr[atlasIndex]; + tiles_[zz] = 0; + ccCArrayRemoveValueAtIndex(atlasIndexArray_, atlasIndex); + [super removeChild:sprite cleanup:cleanup]; +} + +-(void) removeTileAt:(CGPoint)pos +{ + NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position"); + NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released"); + + uint32_t gid = [self tileGIDAt:pos]; + + if( gid ) { + + NSUInteger z = pos.x + pos.y * layerSize_.width; + NSUInteger atlasIndex = [self atlasIndexForExistantZ:z]; + + // remove tile from GID map + tiles_[z] = 0; + + // remove tile from atlas position array + ccCArrayRemoveValueAtIndex(atlasIndexArray_, atlasIndex); + + // remove it from sprites and/or texture atlas + id sprite = [self getChildByTag:z]; + if( sprite ) + [super removeChild:sprite cleanup:YES]; + else { + [textureAtlas_ removeQuadAtIndex:atlasIndex]; + + // update possible children + CCARRAY_FOREACH(children_, sprite) { + NSUInteger ai = [sprite atlasIndex]; + if( ai >= atlasIndex) { + [sprite setAtlasIndex: ai-1]; + } + } + } + } +} + +#pragma mark CCTMXLayer - obtaining positions, offset + +-(CGPoint) calculateLayerOffset:(CGPoint)pos +{ + CGPoint ret = CGPointZero; + switch( layerOrientation_ ) { + case CCTMXOrientationOrtho: + ret = ccp( pos.x * mapTileSize_.width, -pos.y *mapTileSize_.height); + break; + case CCTMXOrientationIso: + ret = ccp( (mapTileSize_.width /2) * (pos.x - pos.y), + (mapTileSize_.height /2 ) * (-pos.x - pos.y) ); + break; + case CCTMXOrientationHex: + NSAssert(CGPointEqualToPoint(pos, CGPointZero), @"offset for hexagonal map not implemented yet"); + break; + } + return ret; +} + +-(CGPoint) positionAt:(CGPoint)pos +{ + CGPoint ret = CGPointZero; + switch( layerOrientation_ ) { + case CCTMXOrientationOrtho: + ret = [self positionForOrthoAt:pos]; + break; + case CCTMXOrientationIso: + ret = [self positionForIsoAt:pos]; + break; + case CCTMXOrientationHex: + ret = [self positionForHexAt:pos]; + break; + } + return ret; +} + +-(CGPoint) positionForOrthoAt:(CGPoint)pos +{ + CGPoint xy = { + pos.x * mapTileSize_.width, + (layerSize_.height - pos.y - 1) * mapTileSize_.height, + }; + return xy; +} + +-(CGPoint) positionForIsoAt:(CGPoint)pos +{ + CGPoint xy = { + mapTileSize_.width /2 * ( layerSize_.width + pos.x - pos.y - 1), + mapTileSize_.height /2 * (( layerSize_.height * 2 - pos.x - pos.y) - 2), + }; + return xy; +} + +-(CGPoint) positionForHexAt:(CGPoint)pos +{ + float diffY = 0; + if( (int)pos.x % 2 == 1 ) + diffY = -mapTileSize_.height/2 ; + + CGPoint xy = { + pos.x * mapTileSize_.width*3/4, + (layerSize_.height - pos.y - 1) * mapTileSize_.height + diffY + }; + return xy; +} + +-(NSInteger) vertexZForPos:(CGPoint)pos +{ + NSInteger ret = 0; + NSUInteger maxVal = 0; + if( useAutomaticVertexZ_ ) { + switch( layerOrientation_ ) { + case CCTMXOrientationIso: + maxVal = layerSize_.width + layerSize_.height; + ret = -(maxVal - (pos.x + pos.y)); + break; + case CCTMXOrientationOrtho: + ret = -(layerSize_.height-pos.y); + break; + case CCTMXOrientationHex: + NSAssert(NO,@"TMX Hexa zOrder not supported"); + break; + default: + NSAssert(NO,@"TMX invalid value"); + break; + } + } else + ret = vertexZvalue_; + + return ret; +} + +#pragma mark CCTMXLayer - draw + +-(void) draw +{ + if( useAutomaticVertexZ_ ) { + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, alphaFuncValue_); + } + + [super draw]; + + if( useAutomaticVertexZ_ ) + glDisable(GL_ALPHA_TEST); +} +@end + diff --git a/libs/cocos2d/CCTMXObjectGroup.h b/libs/cocos2d/CCTMXObjectGroup.h new file mode 100644 index 0000000..79215b3 --- /dev/null +++ b/libs/cocos2d/CCTMXObjectGroup.h @@ -0,0 +1,65 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * Copyright (c) 2010 Neophit + * + * 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. + * + * + * TMX Tiled Map support: + * http://www.mapeditor.org + * + */ + +#import "CCNode.h" + + +@class CCTMXObjectGroup; + + +/** CCTMXObjectGroup represents the TMX object group. +@since v0.99.0 +*/ +@interface CCTMXObjectGroup : NSObject +{ + NSString *groupName_; + CGPoint positionOffset_; + NSMutableArray *objects_; + NSMutableDictionary *properties_; +} + +/** name of the group */ +@property (nonatomic,readwrite,retain) NSString *groupName; +/** offset position of child objects */ +@property (nonatomic,readwrite,assign) CGPoint positionOffset; +/** array of the objects */ +@property (nonatomic,readwrite,retain) NSMutableArray *objects; +/** list of properties stored in a dictionary */ +@property (nonatomic,readwrite,retain) NSMutableDictionary *properties; + +/** return the value for the specific property name */ +-(id) propertyNamed:(NSString *)propertyName; + +/** return the dictionary for the specific object name. + It will return the 1st object found on the array for the given name. + */ +-(NSMutableDictionary*) objectNamed:(NSString *)objectName; + +@end diff --git a/libs/cocos2d/CCTMXObjectGroup.m b/libs/cocos2d/CCTMXObjectGroup.m new file mode 100644 index 0000000..1fe2f63 --- /dev/null +++ b/libs/cocos2d/CCTMXObjectGroup.m @@ -0,0 +1,84 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * Copyright (c) 2010 Neophit + * + * 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. + * + * + * TMX Tiled Map support: + * http://www.mapeditor.org + * + */ + +#import "CCTMXObjectGroup.h" +#import "CCTMXXMLParser.h" +#import "ccMacros.h" +#import "Support/CGPointExtension.h" + + +#pragma mark - +#pragma mark TMXObjectGroup + +@implementation CCTMXObjectGroup + +@synthesize groupName = groupName_; +@synthesize objects = objects_; +@synthesize positionOffset = positionOffset_; +@synthesize properties = properties_; + +-(id) init +{ + if (( self=[super init] )) { + self.groupName = nil; + self.positionOffset = CGPointZero; + self.objects = [NSMutableArray arrayWithCapacity:10]; + self.properties = [NSMutableDictionary dictionaryWithCapacity:5]; + } + return self; +} + +-(void) dealloc +{ + CCLOGINFO( @"cocos2d: deallocing %@", self ); + + [groupName_ release]; + [objects_ release]; + [properties_ release]; + [super dealloc]; +} + +-(NSMutableDictionary*) objectNamed:(NSString *)objectName +{ + for( id object in objects_ ) { + if( [[object valueForKey:@"name"] isEqual:objectName] ) + return object; + } + + // object not found + return nil; +} + +-(id) propertyNamed:(NSString *)propertyName +{ + return [properties_ valueForKey:propertyName]; +} + +@end diff --git a/libs/cocos2d/CCTMXTiledMap.h b/libs/cocos2d/CCTMXTiledMap.h new file mode 100644 index 0000000..163adc2 --- /dev/null +++ b/libs/cocos2d/CCTMXTiledMap.h @@ -0,0 +1,144 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009-2010 Ricardo Quesada + * + * 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. + * + * + * TMX Tiled Map support: + * http://www.mapeditor.org + * + */ + +#import "CCNode.h" + + +@class CCTMXLayer; +@class CCTMXObjectGroup; + +/** Possible oritentations of the TMX map */ +enum +{ + /** Orthogonal orientation */ + CCTMXOrientationOrtho, + + /** Hexagonal orientation */ + CCTMXOrientationHex, + + /** Isometric orientation */ + CCTMXOrientationIso, +}; + +/** CCTMXTiledMap knows how to parse and render a TMX map. + + It adds support for the TMX tiled map format used by http://www.mapeditor.org + It supports isometric, hexagonal and orthogonal tiles. + It also supports object groups, objects, and properties. + + Features: + - Each tile will be treated as an CCSprite + - The sprites are created on demand. They will be created only when you call "[layer tileAt:]" + - Each tile can be rotated / moved / scaled / tinted / "opacitied", since each tile is a CCSprite + - Tiles can be added/removed in runtime + - The z-order of the tiles can be modified in runtime + - Each tile has an anchorPoint of (0,0) + - The anchorPoint of the TMXTileMap is (0,0) + - The TMX layers will be added as a child + - The TMX layers will be aliased by default + - The tileset image will be loaded using the CCTextureCache + - Each tile will have a unique tag + - Each tile will have a unique z value. top-left: z=1, bottom-right: z=max z + - Each object group will be treated as an NSMutableArray + - Object class which will contain all the properties in a dictionary + - Properties can be assigned to the Map, Layer, Object Group, and Object + + Limitations: + - It only supports one tileset per layer. + - Embeded images are not supported + - It only supports the XML format (the JSON format is not supported) + + Technical description: + Each layer is created using an CCTMXLayer (subclass of CCSpriteSheet). If you have 5 layers, then 5 CCTMXLayer will be created, + unless the layer visibility is off. In that case, the layer won't be created at all. + You can obtain the layers (CCTMXLayer objects) at runtime by: + - [map getChildByTag: tag_number]; // 0=1st layer, 1=2nd layer, 2=3rd layer, etc... + - [map layerNamed: name_of_the_layer]; + + Each object group is created using a CCTMXObjectGroup which is a subclass of NSMutableArray. + You can obtain the object groups at runtime by: + - [map objectGroupNamed: name_of_the_object_group]; + + Each object is a CCTMXObject. + + Each property is stored as a key-value pair in an NSMutableDictionary. + You can obtain the properties at runtime by: + + [map propertyNamed: name_of_the_property]; + [layer propertyNamed: name_of_the_property]; + [objectGroup propertyNamed: name_of_the_property]; + [object propertyNamed: name_of_the_property]; + + @since v0.8.1 + */ +@interface CCTMXTiledMap : CCNode +{ + CGSize mapSize_; + CGSize tileSize_; + int mapOrientation_; + NSMutableArray *objectGroups_; + NSMutableDictionary *properties_; + NSMutableDictionary *tileProperties_; +} + +/** the map's size property measured in tiles */ +@property (nonatomic,readonly) CGSize mapSize; +/** the tiles's size property measured in pixels */ +@property (nonatomic,readonly) CGSize tileSize; +/** map orientation */ +@property (nonatomic,readonly) int mapOrientation; +/** object groups */ +@property (nonatomic,readwrite,retain) NSMutableArray *objectGroups; +/** properties */ +@property (nonatomic,readwrite,retain) NSMutableDictionary *properties; + +/** creates a TMX Tiled Map with a TMX file.*/ ++(id) tiledMapWithTMXFile:(NSString*)tmxFile; + +/** initializes a TMX Tiled Map with a TMX file */ +-(id) initWithTMXFile:(NSString*)tmxFile; + +/** return the TMXLayer for the specific layer */ +-(CCTMXLayer*) layerNamed:(NSString *)layerName; + +/** return the TMXObjectGroup for the secific group */ +-(CCTMXObjectGroup*) objectGroupNamed:(NSString *)groupName; + +/** return the TMXObjectGroup for the secific group + @deprecated Use map#objectGroupNamed instead + */ +-(CCTMXObjectGroup*) groupNamed:(NSString *)groupName DEPRECATED_ATTRIBUTE; + +/** return the value for the specific property name */ +-(id) propertyNamed:(NSString *)propertyName; + +/** return properties dictionary for tile GID */ +-(NSDictionary*)propertiesForGID:(unsigned int)GID; +@end + diff --git a/libs/cocos2d/CCTMXTiledMap.m b/libs/cocos2d/CCTMXTiledMap.m new file mode 100644 index 0000000..555dfdd --- /dev/null +++ b/libs/cocos2d/CCTMXTiledMap.m @@ -0,0 +1,200 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009-2010 Ricardo Quesada + * + * 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. + * + * + * TMX Tiled Map support: + * http://www.mapeditor.org + * + */ + +#import "CCTMXTiledMap.h" +#import "CCTMXXMLParser.h" +#import "CCTMXLayer.h" +#import "CCTMXObjectGroup.h" +#import "CCSprite.h" +#import "CCTextureCache.h" +#import "Support/CGPointExtension.h" + + +#pragma mark - +#pragma mark CCTMXTiledMap + +@interface CCTMXTiledMap (Private) +-(id) parseLayer:(CCTMXLayerInfo*)layer map:(CCTMXMapInfo*)mapInfo; +-(CCTMXTilesetInfo*) tilesetForLayer:(CCTMXLayerInfo*)layerInfo map:(CCTMXMapInfo*)mapInfo; +@end + +@implementation CCTMXTiledMap +@synthesize mapSize = mapSize_; +@synthesize tileSize = tileSize_; +@synthesize mapOrientation = mapOrientation_; +@synthesize objectGroups = objectGroups_; +@synthesize properties = properties_; + ++(id) tiledMapWithTMXFile:(NSString*)tmxFile +{ + return [[[self alloc] initWithTMXFile:tmxFile] autorelease]; +} + +-(id) initWithTMXFile:(NSString*)tmxFile +{ + NSAssert(tmxFile != nil, @"TMXTiledMap: tmx file should not bi nil"); + + if ((self=[super init])) { + + [self setContentSize:CGSizeZero]; + + CCTMXMapInfo *mapInfo = [CCTMXMapInfo formatWithTMXFile:tmxFile]; + + NSAssert( [mapInfo.tilesets count] != 0, @"TMXTiledMap: Map not found. Please check the filename."); + + mapSize_ = mapInfo.mapSize; + tileSize_ = mapInfo.tileSize; + mapOrientation_ = mapInfo.orientation; + objectGroups_ = [mapInfo.objectGroups retain]; + properties_ = [mapInfo.properties retain]; + tileProperties_ = [mapInfo.tileProperties retain]; + + int idx=0; + + for( CCTMXLayerInfo *layerInfo in mapInfo.layers ) { + + if( layerInfo.visible ) { + CCNode *child = [self parseLayer:layerInfo map:mapInfo]; + [self addChild:child z:idx tag:idx]; + + // update content size with the max size + CGSize childSize = [child contentSize]; + CGSize currentSize = [self contentSize]; + currentSize.width = MAX( currentSize.width, childSize.width ); + currentSize.height = MAX( currentSize.height, childSize.height ); + [self setContentSize:currentSize]; + + idx++; + } + } + } + + return self; +} + +-(void) dealloc +{ + [objectGroups_ release]; + [properties_ release]; + [tileProperties_ release]; + [super dealloc]; +} + +// private +-(id) parseLayer:(CCTMXLayerInfo*)layerInfo map:(CCTMXMapInfo*)mapInfo +{ + CCTMXTilesetInfo *tileset = [self tilesetForLayer:layerInfo map:mapInfo]; + CCTMXLayer *layer = [CCTMXLayer layerWithTilesetInfo:tileset layerInfo:layerInfo mapInfo:mapInfo]; + + // tell the layerinfo to release the ownership of the tiles map. + layerInfo.ownTiles = NO; + + [layer setupTiles]; + + return layer; +} + +-(CCTMXTilesetInfo*) tilesetForLayer:(CCTMXLayerInfo*)layerInfo map:(CCTMXMapInfo*)mapInfo +{ + CFByteOrder o = CFByteOrderGetCurrent(); + + CGSize size = layerInfo.layerSize; + + id iter = [mapInfo.tilesets reverseObjectEnumerator]; + for( CCTMXTilesetInfo* tileset in iter) { + for( unsigned int y = 0; y < size.height; y++ ) { + for( unsigned int x = 0; x < size.width; x++ ) { + + unsigned int pos = x + size.width * y; + unsigned int gid = layerInfo.tiles[ pos ]; + + // gid are stored in little endian. + // if host is big endian, then swap + if( o == CFByteOrderBigEndian ) + gid = CFSwapInt32( gid ); + + // XXX: gid == 0 --> empty tile + if( gid != 0 ) { + + // Optimization: quick return + // if the layer is invalid (more than 1 tileset per layer) an assert will be thrown later + if( gid >= tileset.firstGid ) + return tileset; + } + } + } + } + + // If all the tiles are 0, return empty tileset + CCLOG(@"cocos2d: Warning: TMX Layer '%@' has no tiles", layerInfo.name); + return nil; +} + + +// public + +-(CCTMXLayer*) layerNamed:(NSString *)layerName +{ + CCTMXLayer *layer; + CCARRAY_FOREACH(children_, layer) { + if([layer isKindOfClass:[CCTMXLayer class]]) + if([layer.layerName isEqual:layerName]) + return layer; + } + + // layer not found + return nil; +} + +-(CCTMXObjectGroup*) objectGroupNamed:(NSString *)groupName +{ + for( CCTMXObjectGroup *objectGroup in objectGroups_ ) { + if( [objectGroup.groupName isEqual:groupName] ) + return objectGroup; + } + + // objectGroup not found + return nil; +} + +// XXX deprecated +-(CCTMXObjectGroup*) groupNamed:(NSString *)groupName +{ + return [self objectGroupNamed:groupName]; +} + +-(id) propertyNamed:(NSString *)propertyName +{ + return [properties_ valueForKey:propertyName]; +} +-(NSDictionary*)propertiesForGID:(unsigned int)GID{ + return [tileProperties_ objectForKey:[NSNumber numberWithInt:GID]]; +} +@end + diff --git a/libs/cocos2d/CCTMXXMLParser.h b/libs/cocos2d/CCTMXXMLParser.h new file mode 100644 index 0000000..a8617f0 --- /dev/null +++ b/libs/cocos2d/CCTMXXMLParser.h @@ -0,0 +1,201 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009-2010 Ricardo Quesada + * + * 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. + * + * + * TMX Tiled Map support: + * http://www.mapeditor.org + * + */ + +/* + * Internal TMX parser + * + * IMPORTANT: These classed should not be documented using doxygen strings + * since the user should not use them. + * + */ + + +#import +#import + +enum { + TMXLayerAttribNone = 1 << 0, + TMXLayerAttribBase64 = 1 << 1, + TMXLayerAttribGzip = 1 << 2, + TMXLayerAttribZlib = 1 << 3, +}; + +enum { + TMXPropertyNone, + TMXPropertyMap, + TMXPropertyLayer, + TMXPropertyObjectGroup, + TMXPropertyObject, + TMXPropertyTile +}; + +/* CCTMXLayerInfo contains the information about the layers like: + - Layer name + - Layer size + - Layer opacity at creation time (it can be modified at runtime) + - Whether the layer is visible (if it's not visible, then the CocosNode won't be created) + + This information is obtained from the TMX file. + */ +@interface CCTMXLayerInfo : NSObject +{ + NSString *name_; + CGSize layerSize_; + unsigned int *tiles_; + BOOL visible_; + unsigned char opacity_; + BOOL ownTiles_; + unsigned int minGID_; + unsigned int maxGID_; + NSMutableDictionary *properties_; + CGPoint offset_; +} + +@property (nonatomic,readwrite,retain) NSString *name; +@property (nonatomic,readwrite) CGSize layerSize; +@property (nonatomic,readwrite) unsigned int *tiles; +@property (nonatomic,readwrite) BOOL visible; +@property (nonatomic,readwrite) unsigned char opacity; +@property (nonatomic,readwrite) BOOL ownTiles; +@property (nonatomic,readwrite) unsigned int minGID; +@property (nonatomic,readwrite) unsigned int maxGID; +@property (nonatomic,readwrite,retain) NSMutableDictionary *properties; +@property (nonatomic,readwrite) CGPoint offset; +@end + +/* CCTMXTilesetInfo contains the information about the tilesets like: + - Tileset name + - Tilset spacing + - Tileset margin + - size of the tiles + - Image used for the tiles + - Image size + + This information is obtained from the TMX file. + */ +@interface CCTMXTilesetInfo : NSObject +{ + NSString *name_; + unsigned int firstGid_; + CGSize tileSize_; + unsigned int spacing_; + unsigned int margin_; + + // filename containing the tiles (should be spritesheet / texture atlas) + NSString *sourceImage_; + + // size in pixels of the image + CGSize imageSize_; +} +@property (nonatomic,readwrite,retain) NSString *name; +@property (nonatomic,readwrite,assign) unsigned int firstGid; +@property (nonatomic,readwrite,assign) CGSize tileSize; +@property (nonatomic,readwrite,assign) unsigned int spacing; +@property (nonatomic,readwrite,assign) unsigned int margin; +@property (nonatomic,readwrite,retain) NSString *sourceImage; +@property (nonatomic,readwrite,assign) CGSize imageSize; + +-(CGRect) rectForGID:(unsigned int)gid; +@end + +/* CCTMXMapInfo contains the information about the map like: + - Map orientation (hexagonal, isometric or orthogonal) + - Tile size + - Map size + + And it also contains: + - Layers (an array of TMXLayerInfo objects) + - Tilesets (an array of TMXTilesetInfo objects) + - ObjectGroups (an array of TMXObjectGroupInfo objects) + + This information is obtained from the TMX file. + + */ +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#if defined(__IPHONE_4_0) +@interface CCTMXMapInfo : NSObject +#else +@interface CCTMXMapInfo : NSObject +#endif + +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +@interface CCTMXMapInfo : NSObject +#endif +{ + NSMutableString *currentString; + BOOL storingCharacters; + int layerAttribs; + int parentElement; + unsigned int parentGID_; + + + // tmx filename + NSString *filename_; + + // map orientation + int orientation_; + + // map width & height + CGSize mapSize_; + + // tiles width & height + CGSize tileSize_; + + // Layers + NSMutableArray *layers_; + + // tilesets + NSMutableArray *tilesets_; + + // ObjectGroups + NSMutableArray *objectGroups_; + + // properties + NSMutableDictionary *properties_; + + // tile properties + NSMutableDictionary *tileProperties_; +} + +@property (nonatomic,readwrite,assign) int orientation; +@property (nonatomic,readwrite,assign) CGSize mapSize; +@property (nonatomic,readwrite,assign) CGSize tileSize; +@property (nonatomic,readwrite,retain) NSMutableArray *layers; +@property (nonatomic,readwrite,retain) NSMutableArray *tilesets; +@property (nonatomic,readwrite,retain) NSString *filename; +@property (nonatomic,readwrite,retain) NSMutableArray *objectGroups; +@property (nonatomic,readwrite,retain) NSMutableDictionary *properties; +@property (nonatomic,readwrite,retain) NSMutableDictionary *tileProperties; + +/** creates a TMX Format with a tmx file */ ++(id) formatWithTMXFile:(NSString*)tmxFile; +/** initializes a TMX format witha tmx file */ +-(id) initWithTMXFile:(NSString*)tmxFile; +@end + diff --git a/libs/cocos2d/CCTMXXMLParser.m b/libs/cocos2d/CCTMXXMLParser.m new file mode 100644 index 0000000..ac254dc --- /dev/null +++ b/libs/cocos2d/CCTMXXMLParser.m @@ -0,0 +1,447 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009-2010 Ricardo Quesada + * + * 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. + * + * + * TMX Tiled Map support: + * http://www.mapeditor.org + * + */ + + +#import +#include + +#import "ccMacros.h" +#import "Support/CGPointExtension.h" +#import "CCTMXXMLParser.h" +#import "CCTMXTiledMap.h" +#import "CCTMXObjectGroup.h" +#import "Support/base64.h" +#import "Support/ZipUtils.h" +#import "Support/CCFileUtils.h" + +#pragma mark - +#pragma mark TMXLayerInfo + + +@implementation CCTMXLayerInfo + +@synthesize name = name_, layerSize = layerSize_, tiles = tiles_, visible = visible_, opacity = opacity_, ownTiles = ownTiles_, minGID = minGID_, maxGID = maxGID_, properties = properties_; +@synthesize offset = offset_; +-(id) init +{ + if( (self=[super init])) { + ownTiles_ = YES; + minGID_ = 100000; + maxGID_ = 0; + self.name = nil; + tiles_ = NULL; + offset_ = CGPointZero; + self.properties = [NSMutableDictionary dictionaryWithCapacity:5]; + } + return self; +} +- (void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@",self); + + [name_ release]; + [properties_ release]; + + if( ownTiles_ && tiles_ ) { + free( tiles_ ); + tiles_ = NULL; + } + [super dealloc]; +} + +@end + +#pragma mark - +#pragma mark TMXTilesetInfo +@implementation CCTMXTilesetInfo + +@synthesize name = name_, firstGid = firstGid_, tileSize = tileSize_, spacing = spacing_, margin = margin_, sourceImage = sourceImage_, imageSize = imageSize_; + +- (void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + [sourceImage_ release]; + [name_ release]; + [super dealloc]; +} + +-(CGRect) rectForGID:(unsigned int)gid +{ + CGRect rect; + rect.size = tileSize_; + + gid = gid - firstGid_; + + int max_x = (imageSize_.width - margin_*2 + spacing_) / (tileSize_.width + spacing_); + // int max_y = (imageSize.height - margin*2 + spacing) / (tileSize.height + spacing); + + rect.origin.x = (gid % max_x) * (tileSize_.width + spacing_) + margin_; + rect.origin.y = (gid / max_x) * (tileSize_.height + spacing_) + margin_; + + return rect; +} +@end + +#pragma mark - +#pragma mark CCTMXMapInfo + +@interface CCTMXMapInfo (Private) +/* initalises parsing of an XML file, either a tmx (Map) file or tsx (Tileset) file */ +-(void) parseXMLFile:(NSString *)xmlFilename; +@end + + +@implementation CCTMXMapInfo + +@synthesize orientation = orientation_, mapSize = mapSize_, layers = layers_, tilesets = tilesets_, tileSize = tileSize_, filename = filename_, objectGroups = objectGroups_, properties = properties_; +@synthesize tileProperties = tileProperties_; + ++(id) formatWithTMXFile:(NSString*)tmxFile +{ + return [[[self alloc] initWithTMXFile:tmxFile] autorelease]; +} + +-(id) initWithTMXFile:(NSString*)tmxFile +{ + if( (self=[super init])) { + + self.tilesets = [NSMutableArray arrayWithCapacity:4]; + self.layers = [NSMutableArray arrayWithCapacity:4]; + self.filename = tmxFile; + self.objectGroups = [NSMutableArray arrayWithCapacity:4]; + self.properties = [NSMutableDictionary dictionaryWithCapacity:5]; + self.tileProperties = [NSMutableDictionary dictionaryWithCapacity:5]; + + // tmp vars + currentString = [[NSMutableString alloc] initWithCapacity:1024]; + storingCharacters = NO; + layerAttribs = TMXLayerAttribNone; + parentElement = TMXPropertyNone; + + [self parseXMLFile:filename_]; + } + return self; +} +- (void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + [tilesets_ release]; + [layers_ release]; + [filename_ release]; + [currentString release]; + [objectGroups_ release]; + [properties_ release]; + [tileProperties_ release]; + [super dealloc]; +} + +- (void) parseXMLFile:(NSString *)xmlFilename +{ + NSURL *url = [NSURL fileURLWithPath:[CCFileUtils fullPathFromRelativePath:xmlFilename] ]; + NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url]; + + // we'll do the parsing + [parser setDelegate:self]; + [parser setShouldProcessNamespaces:NO]; + [parser setShouldReportNamespacePrefixes:NO]; + [parser setShouldResolveExternalEntities:NO]; + [parser parse]; + + NSAssert1( ! [parser parserError], @"Error parsing file: %@.", xmlFilename ); + + [parser release]; +} + +// the XML parser calls here with all the elements +-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict +{ + if([elementName isEqualToString:@"map"]) { + NSString *version = [attributeDict valueForKey:@"version"]; + if( ! [version isEqualToString:@"1.0"] ) + CCLOG(@"cocos2d: TMXFormat: Unsupported TMX version: %@", version); + NSString *orientationStr = [attributeDict valueForKey:@"orientation"]; + if( [orientationStr isEqualToString:@"orthogonal"]) + orientation_ = CCTMXOrientationOrtho; + else if ( [orientationStr isEqualToString:@"isometric"]) + orientation_ = CCTMXOrientationIso; + else if( [orientationStr isEqualToString:@"hexagonal"]) + orientation_ = CCTMXOrientationHex; + else + CCLOG(@"cocos2d: TMXFomat: Unsupported orientation: %@", orientation_); + + mapSize_.width = [[attributeDict valueForKey:@"width"] intValue]; + mapSize_.height = [[attributeDict valueForKey:@"height"] intValue]; + tileSize_.width = [[attributeDict valueForKey:@"tilewidth"] intValue]; + tileSize_.height = [[attributeDict valueForKey:@"tileheight"] intValue]; + + // The parent element is now "map" + parentElement = TMXPropertyMap; + } else if([elementName isEqualToString:@"tileset"]) { + + // If this is an external tileset then start parsing that + NSString *externalTilesetFilename = [attributeDict valueForKey:@"source"]; + if (externalTilesetFilename) { + // Tileset file will be relative to the map file. So we need to convert it to an absolute path + NSString *dir = [filename_ stringByDeletingLastPathComponent]; // Directory of map file + externalTilesetFilename = [dir stringByAppendingPathComponent:externalTilesetFilename]; // Append path to tileset file + + [self parseXMLFile:externalTilesetFilename]; + } else { + + CCTMXTilesetInfo *tileset = [CCTMXTilesetInfo new]; + tileset.name = [attributeDict valueForKey:@"name"]; + tileset.firstGid = [[attributeDict valueForKey:@"firstgid"] intValue]; + tileset.spacing = [[attributeDict valueForKey:@"spacing"] intValue]; + tileset.margin = [[attributeDict valueForKey:@"margin"] intValue]; + CGSize s; + s.width = [[attributeDict valueForKey:@"tilewidth"] intValue]; + s.height = [[attributeDict valueForKey:@"tileheight"] intValue]; + tileset.tileSize = s; + + [tilesets_ addObject:tileset]; + [tileset release]; + } + + }else if([elementName isEqualToString:@"tile"]){ + CCTMXTilesetInfo* info = [tilesets_ lastObject]; + NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:3]; + parentGID_ = [info firstGid] + [[attributeDict valueForKey:@"id"] intValue]; + [tileProperties_ setObject:dict forKey:[NSNumber numberWithInt:parentGID_]]; + + parentElement = TMXPropertyTile; + + }else if([elementName isEqualToString:@"layer"]) { + CCTMXLayerInfo *layer = [CCTMXLayerInfo new]; + layer.name = [attributeDict valueForKey:@"name"]; + + CGSize s; + s.width = [[attributeDict valueForKey:@"width"] intValue]; + s.height = [[attributeDict valueForKey:@"height"] intValue]; + layer.layerSize = s; + + layer.visible = ![[attributeDict valueForKey:@"visible"] isEqualToString:@"0"]; + + if( [attributeDict valueForKey:@"opacity"] ) + layer.opacity = 255 * [[attributeDict valueForKey:@"opacity"] floatValue]; + else + layer.opacity = 255; + + int x = [[attributeDict valueForKey:@"x"] intValue]; + int y = [[attributeDict valueForKey:@"y"] intValue]; + layer.offset = ccp(x,y); + + [layers_ addObject:layer]; + [layer release]; + + // The parent element is now "layer" + parentElement = TMXPropertyLayer; + + } else if([elementName isEqualToString:@"objectgroup"]) { + + CCTMXObjectGroup *objectGroup = [[CCTMXObjectGroup alloc] init]; + objectGroup.groupName = [attributeDict valueForKey:@"name"]; + CGPoint positionOffset; + positionOffset.x = [[attributeDict valueForKey:@"x"] intValue] * tileSize_.width; + positionOffset.y = [[attributeDict valueForKey:@"y"] intValue] * tileSize_.height; + objectGroup.positionOffset = positionOffset; + + [objectGroups_ addObject:objectGroup]; + [objectGroup release]; + + // The parent element is now "objectgroup" + parentElement = TMXPropertyObjectGroup; + + } else if([elementName isEqualToString:@"image"]) { + + CCTMXTilesetInfo *tileset = [tilesets_ lastObject]; + + // build full path + NSString *imagename = [attributeDict valueForKey:@"source"]; + NSString *path = [filename_ stringByDeletingLastPathComponent]; + tileset.sourceImage = [path stringByAppendingPathComponent:imagename]; + + } else if([elementName isEqualToString:@"data"]) { + NSString *encoding = [attributeDict valueForKey:@"encoding"]; + NSString *compression = [attributeDict valueForKey:@"compression"]; + + if( [encoding isEqualToString:@"base64"] ) { + layerAttribs |= TMXLayerAttribBase64; + storingCharacters = YES; + + if( [compression isEqualToString:@"gzip"] ) + layerAttribs |= TMXLayerAttribGzip; + + else if( [compression isEqualToString:@"zlib"] ) + layerAttribs |= TMXLayerAttribZlib; + + NSAssert( !compression || [compression isEqualToString:@"gzip"] || [compression isEqualToString:@"zlib"], @"TMX: unsupported compression method" ); + } + + NSAssert( layerAttribs != TMXLayerAttribNone, @"TMX tile map: Only base64 and/or gzip/zlib maps are supported" ); + + } else if([elementName isEqualToString:@"object"]) { + + CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject]; + + // The value for "type" was blank or not a valid class name + // Create an instance of TMXObjectInfo to store the object and its properties + NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:5]; + + // Set the name of the object to the value for "name" + [dict setValue:[attributeDict valueForKey:@"name"] forKey:@"name"]; + + // Assign all the attributes as key/name pairs in the properties dictionary + [dict setValue:[attributeDict valueForKey:@"type"] forKey:@"type"]; + int x = [[attributeDict valueForKey:@"x"] intValue] + objectGroup.positionOffset.x; + [dict setValue:[NSNumber numberWithInt:x] forKey:@"x"]; + int y = [[attributeDict valueForKey:@"y"] intValue] + objectGroup.positionOffset.y; + // Correct y position. (Tiled uses Flipped, cocos2d uses Standard) + y = (mapSize_.height * tileSize_.height) - y - [[attributeDict valueForKey:@"height"] intValue]; + [dict setValue:[NSNumber numberWithInt:y] forKey:@"y"]; + [dict setValue:[attributeDict valueForKey:@"width"] forKey:@"width"]; + [dict setValue:[attributeDict valueForKey:@"height"] forKey:@"height"]; + + // Add the object to the objectGroup + [[objectGroup objects] addObject:dict]; + [dict release]; + + // The parent element is now "object" + parentElement = TMXPropertyObject; + + } else if([elementName isEqualToString:@"property"]) { + + if ( parentElement == TMXPropertyNone ) { + + CCLOG( @"TMX tile map: Parent element is unsupported. Cannot add property named '%@' with value '%@'", + [attributeDict valueForKey:@"name"], [attributeDict valueForKey:@"value"] ); + + } else if ( parentElement == TMXPropertyMap ) { + + // The parent element is the map + [properties_ setValue:[attributeDict valueForKey:@"value"] forKey:[attributeDict valueForKey:@"name"]]; + + } else if ( parentElement == TMXPropertyLayer ) { + + // The parent element is the last layer + CCTMXLayerInfo *layer = [layers_ lastObject]; + // Add the property to the layer + [[layer properties] setValue:[attributeDict valueForKey:@"value"] forKey:[attributeDict valueForKey:@"name"]]; + + } else if ( parentElement == TMXPropertyObjectGroup ) { + + // The parent element is the last object group + CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject]; + [[objectGroup properties] setValue:[attributeDict valueForKey:@"value"] forKey:[attributeDict valueForKey:@"name"]]; + + } else if ( parentElement == TMXPropertyObject ) { + + // The parent element is the last object + CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject]; + NSMutableDictionary *dict = [[objectGroup objects] lastObject]; + + NSString *propertyName = [attributeDict valueForKey:@"name"]; + NSString *propertyValue = [attributeDict valueForKey:@"value"]; + + [dict setValue:propertyValue forKey:propertyName]; + } else if ( parentElement == TMXPropertyTile ) { + + NSMutableDictionary* dict = [tileProperties_ objectForKey:[NSNumber numberWithInt:parentGID_]]; + NSString *propertyName = [attributeDict valueForKey:@"name"]; + NSString *propertyValue = [attributeDict valueForKey:@"value"]; + [dict setObject:propertyValue forKey:propertyName]; + + } + } +} + +- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName +{ + int len = 0; + + if([elementName isEqualToString:@"data"] && layerAttribs&TMXLayerAttribBase64) { + storingCharacters = NO; + + CCTMXLayerInfo *layer = [layers_ lastObject]; + + unsigned char *buffer; + len = base64Decode((unsigned char*)[currentString UTF8String], (unsigned int) [currentString length], &buffer); + if( ! buffer ) { + CCLOG(@"cocos2d: TiledMap: decode data error"); + return; + } + + if( layerAttribs & (TMXLayerAttribGzip | TMXLayerAttribZlib) ) { + unsigned char *deflated; + ccInflateMemory(buffer, len, &deflated); + free( buffer ); + + if( ! deflated ) { + CCLOG(@"cocos2d: TiledMap: inflate data error"); + return; + } + + layer.tiles = (unsigned int*) deflated; + } else + layer.tiles = (unsigned int*) buffer; + + [currentString setString:@""]; + + } else if ([elementName isEqualToString:@"map"]) { + // The map element has ended + parentElement = TMXPropertyNone; + + } else if ([elementName isEqualToString:@"layer"]) { + // The layer element has ended + parentElement = TMXPropertyNone; + + } else if ([elementName isEqualToString:@"objectgroup"]) { + // The objectgroup element has ended + parentElement = TMXPropertyNone; + + } else if ([elementName isEqualToString:@"object"]) { + // The object element has ended + parentElement = TMXPropertyNone; + } +} + +- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string +{ + if (storingCharacters) + [currentString appendString:string]; +} + + +// +// the level did not load, file not found, etc. +// +-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{ + CCLOG(@"cocos2d: Error on XML Parse: %@", [parseError localizedDescription] ); +} + +@end diff --git a/libs/cocos2d/CCTexture2D.h b/libs/cocos2d/CCTexture2D.h new file mode 100644 index 0000000..7f606f5 --- /dev/null +++ b/libs/cocos2d/CCTexture2D.h @@ -0,0 +1,308 @@ +/* + +===== IMPORTANT ===== + +This is sample code demonstrating API, technology or techniques in development. +Although this sample code has been reviewed for technical accuracy, it is not +final. Apple is supplying this information to help you plan for the adoption of +the technologies and programming interfaces described herein. This information +is subject to change, and software implemented based on this sample code should +be tested with final operating system software and final documentation. Newer +versions of this sample code may be provided with future seeds of the API or +technology. For information about updates to this and other developer +documentation, view the New & Updated sidebars in subsequent documentation +seeds. + +===================== + +File: Texture2D.h +Abstract: Creates OpenGL 2D textures from images or text. + +Version: 1.6 + +Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. +("Apple") in consideration of your agreement to the following terms, and your +use, installation, modification or redistribution of this Apple software +constitutes acceptance of these terms. If you do not agree with these terms, +please do not use, install, modify or redistribute this Apple software. + +In consideration of your agreement to abide by the following terms, and subject +to these terms, Apple grants you a personal, non-exclusive license, under +Apple's copyrights in this original Apple software (the "Apple Software"), to +use, reproduce, modify and redistribute the Apple Software, with or without +modifications, in source and/or binary forms; provided that if you redistribute +the Apple Software in its entirety and without modifications, you must retain +this notice and the following text and disclaimers in all such redistributions +of the Apple Software. +Neither the name, trademarks, service marks or logos of Apple Inc. may be used +to endorse or promote products derived from the Apple Software without specific +prior written permission from Apple. Except as expressly stated in this notice, +no other rights or licenses, express or implied, are granted by Apple herein, +including but not limited to any patent rights that may be infringed by your +derivative works or by other works in which the Apple Software may be +incorporated. + +The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO +WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED +WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN +COMBINATION WITH YOUR PRODUCTS. + +IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR +DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF +CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Copyright (C) 2008 Apple Inc. All Rights Reserved. + +*/ + +#import + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import // for UIImage +#endif + +#import // for NSObject + +#import "Platforms/CCGL.h" // OpenGL stuff +#import "Platforms/CCNS.h" // Next-Step stuff + +//CONSTANTS: + +/** @typedef CCTexture2DPixelFormat + Possible texture pixel formats + */ +typedef enum { + kCCTexture2DPixelFormat_Automatic = 0, + //! 32-bit texture: RGBA8888 + kCCTexture2DPixelFormat_RGBA8888, + //! 16-bit texture without Alpha channel + kCCTexture2DPixelFormat_RGB565, + //! 8-bit textures used as masks + kCCTexture2DPixelFormat_A8, + //! 16-bit textures: RGBA4444 + kCCTexture2DPixelFormat_RGBA4444, + //! 16-bit textures: RGB5A1 + kCCTexture2DPixelFormat_RGB5A1, + + //! Default texture format: RGBA8888 + kCCTexture2DPixelFormat_Default = kCCTexture2DPixelFormat_RGBA8888, + + // backward compatibility stuff + kTexture2DPixelFormat_Automatic = kCCTexture2DPixelFormat_Automatic, + kTexture2DPixelFormat_RGBA8888 = kCCTexture2DPixelFormat_RGBA8888, + kTexture2DPixelFormat_RGB565 = kCCTexture2DPixelFormat_RGB565, + kTexture2DPixelFormat_A8 = kCCTexture2DPixelFormat_A8, + kTexture2DPixelFormat_RGBA4444 = kCCTexture2DPixelFormat_RGBA4444, + kTexture2DPixelFormat_RGB5A1 = kCCTexture2DPixelFormat_RGB5A1, + kTexture2DPixelFormat_Default = kCCTexture2DPixelFormat_Default + +} CCTexture2DPixelFormat; + +//CLASS INTERFACES: + +/** CCTexture2D class. + * This class allows to easily create OpenGL 2D textures from images, text or raw data. + * The created CCTexture2D object will always have power-of-two dimensions. + * Depending on how you create the CCTexture2D object, the actual image area of the texture might be smaller than the texture dimensions i.e. "contentSize" != (pixelsWide, pixelsHigh) and (maxS, maxT) != (1.0, 1.0). + * Be aware that the content of the generated textures will be upside-down! + */ +@interface CCTexture2D : NSObject +{ + GLuint name_; + CGSize size_; + NSUInteger width_, + height_; + CCTexture2DPixelFormat format_; + GLfloat maxS_, + maxT_; + BOOL hasPremultipliedAlpha_; +} +/** Intializes with a texture2d with data */ +- (id) initWithData:(const void*)data pixelFormat:(CCTexture2DPixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size; + +/** These functions are needed to create mutable textures */ +- (void) releaseData:(void*)data; +- (void*) keepData:(void*)data length:(NSUInteger)length; + +/** pixel format of the texture */ +@property(nonatomic,readonly) CCTexture2DPixelFormat pixelFormat; +/** width in pixels */ +@property(nonatomic,readonly) NSUInteger pixelsWide; +/** hight in pixels */ +@property(nonatomic,readonly) NSUInteger pixelsHigh; + +/** texture name */ +@property(nonatomic,readonly) GLuint name; + +/** returns content size of the texture in pixels */ +@property(nonatomic,readonly, nonatomic) CGSize contentSizeInPixels; + +/** texture max S */ +@property(nonatomic,readwrite) GLfloat maxS; +/** texture max T */ +@property(nonatomic,readwrite) GLfloat maxT; +/** whether or not the texture has their Alpha premultiplied */ +@property(nonatomic,readonly) BOOL hasPremultipliedAlpha; + +/** returns the content size of the texture in points */ +-(CGSize) contentSize; +@end + +/** +Drawing extensions to make it easy to draw basic quads using a CCTexture2D object. +These functions require GL_TEXTURE_2D and both GL_VERTEX_ARRAY and GL_TEXTURE_COORD_ARRAY client states to be enabled. +*/ +@interface CCTexture2D (Drawing) +/** draws a texture at a given point */ +- (void) drawAtPoint:(CGPoint)point; +/** draws a texture inside a rect */ +- (void) drawInRect:(CGRect)rect; +@end + +/** +Extensions to make it easy to create a CCTexture2D object from an image file. +Note that RGBA type textures will have their alpha premultiplied - use the blending mode (GL_ONE, GL_ONE_MINUS_SRC_ALPHA). +*/ +@interface CCTexture2D (Image) +/** Initializes a texture from a UIImage object */ +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +- (id) initWithImage:(UIImage *)uiImage; +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +- (id) initWithImage:(CGImageRef)cgImage; +#endif +@end + +/** +Extensions to make it easy to create a CCTexture2D object from a string of text. +Note that the generated textures are of type A8 - use the blending mode (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). +*/ +@interface CCTexture2D (Text) +/** Initializes a texture from a string with dimensions, alignment, font name and font size */ +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size; +/** Initializes a texture from a string with font name and font size */ +- (id) initWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size; +@end + + +/** + Extensions to make it easy to create a CCTexture2D object from a PVRTC file + Note that the generated textures don't have their alpha premultiplied - use the blending mode (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). + */ +@interface CCTexture2D (PVRSupport) +/** Initializes a texture from a PVR Texture Compressed (PVRTC) buffer + * + * IMPORTANT: This method is only defined on iOS. It is not supported on the Mac version. + */ +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +-(id) initWithPVRTCData: (const void*)data level:(int)level bpp:(int)bpp hasAlpha:(BOOL)hasAlpha length:(int)length; +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED +/** Initializes a texture from a PVR file. + + Supported PVR formats: + - BGRA 8888 + - RGBA 8888 + - RGBA 4444 + - RGBA 5551 + - RBG 565 + - A 8 + - I 8 + - AI 8 + - PVRTC 2BPP + - PVRTC 4BPP + + By default PVR images are treated as if they alpha channel is NOT premultiplied. You can override this behavior with this class method: + - PVRImagesHavePremultipliedAlpha:(BOOL)haveAlphaPremultiplied; + + IMPORTANT: This method is only defined on iOS. It is not supported on the Mac version. + + */ +-(id) initWithPVRFile: (NSString*) file; + +/** treats (or not) PVR files as if they have alpha premultiplied. + Since it is impossible to know at runtime if the PVR images have the alpha channel premultiplied, it is + possible load them as if they have (or not) the alpha channel premultiplied. + + By default it is disabled. + + @since v0.99.5 + */ ++(void) PVRImagesHavePremultipliedAlpha:(BOOL)haveAlphaPremultiplied; +@end + +/** + Extension to set the Min / Mag filter + */ +typedef struct _ccTexParams { + GLuint minFilter; + GLuint magFilter; + GLuint wrapS; + GLuint wrapT; +} ccTexParams; + +@interface CCTexture2D (GLFilter) +/** sets the min filter, mag filter, wrap s and wrap t texture parameters. + If the texture size is NPOT (non power of 2), then in can only use GL_CLAMP_TO_EDGE in GL_TEXTURE_WRAP_{S,T}. + @since v0.8 + */ +-(void) setTexParameters: (ccTexParams*) texParams; + +/** sets antialias texture parameters: + - GL_TEXTURE_MIN_FILTER = GL_LINEAR + - GL_TEXTURE_MAG_FILTER = GL_LINEAR + + @since v0.8 + */ +- (void) setAntiAliasTexParameters; + +/** sets alias texture parameters: + - GL_TEXTURE_MIN_FILTER = GL_NEAREST + - GL_TEXTURE_MAG_FILTER = GL_NEAREST + + @since v0.8 + */ +- (void) setAliasTexParameters; + + +/** Generates mipmap images for the texture. + It only works if the texture size is POT (power of 2). + @since v0.99.0 + */ +-(void) generateMipmap; + + +@end + +@interface CCTexture2D (PixelFormat) +/** sets the default pixel format for UIImages that contains alpha channel. + If the UIImage contains alpha channel, then the options are: + - generate 32-bit textures: kCCTexture2DPixelFormat_RGBA8888 (default one) + - generate 16-bit textures: kCCTexture2DPixelFormat_RGBA4444 + - generate 16-bit textures: kCCTexture2DPixelFormat_RGB5A1 + - generate 16-bit textures: kCCTexture2DPixelFormat_RGB565 + - generate 8-bit textures: kCCTexture2DPixelFormat_A8 (only use it if you use just 1 color) + + How does it work ? + - If the image is an RGBA (with Alpha) then the default pixel format will be used (it can be a 8-bit, 16-bit or 32-bit texture) + - If the image is an RGB (without Alpha) then an RGB565 texture will be used (16-bit texture) + + This parameter is not valid for PVR images. + + @since v0.8 + */ ++(void) setDefaultAlphaPixelFormat:(CCTexture2DPixelFormat)format; + +/** returns the alpha pixel format + @since v0.8 + */ ++(CCTexture2DPixelFormat) defaultAlphaPixelFormat; +@end + + + + + diff --git a/libs/cocos2d/CCTexture2D.m b/libs/cocos2d/CCTexture2D.m new file mode 100644 index 0000000..2b571d2 --- /dev/null +++ b/libs/cocos2d/CCTexture2D.m @@ -0,0 +1,754 @@ +/* + +===== IMPORTANT ===== + +This is sample code demonstrating API, technology or techniques in development. +Although this sample code has been reviewed for technical accuracy, it is not +final. Apple is supplying this information to help you plan for the adoption of +the technologies and programming interfaces described herein. This information +is subject to change, and software implemented based on this sample code should +be tested with final operating system software and final documentation. Newer +versions of this sample code may be provided with future seeds of the API or +technology. For information about updates to this and other developer +documentation, view the New & Updated sidebars in subsequent documentationd +seeds. + +===================== + +File: Texture2D.m +Abstract: Creates OpenGL 2D textures from images or text. + +Version: 1.6 + +Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. +("Apple") in consideration of your agreement to the following terms, and your +use, installation, modification or redistribution of this Apple software +constitutes acceptance of these terms. If you do not agree with these terms, +please do not use, install, modify or redistribute this Apple software. + +In consideration of your agreement to abide by the following terms, and subject +to these terms, Apple grants you a personal, non-exclusive license, under +Apple's copyrights in this original Apple software (the "Apple Software"), to +use, reproduce, modify and redistribute the Apple Software, with or without +modifications, in source and/or binary forms; provided that if you redistribute +the Apple Software in its entirety and without modifications, you must retain +this notice and the following text and disclaimers in all such redistributions +of the Apple Software. +Neither the name, trademarks, service marks or logos of Apple Inc. may be used +to endorse or promote products derived from the Apple Software without specific +prior written permission from Apple. Except as expressly stated in this notice, +no other rights or licenses, express or implied, are granted by Apple herein, +including but not limited to any patent rights that may be infringed by your +derivative works or by other works in which the Apple Software may be +incorporated. + +The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO +WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED +WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN +COMBINATION WITH YOUR PRODUCTS. + +IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR +DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF +CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Copyright (C) 2008 Apple Inc. All Rights Reserved. + +*/ + +/* + * Support for RGBA_4_4_4_4 and RGBA_5_5_5_1 was copied from: + * https://devforums.apple.com/message/37855#37855 by a1studmuffin + */ + + +#import + +#import "Platforms/CCGL.h" +#import "Platforms/CCNS.h" + + +#import "CCTexture2D.h" +#import "ccConfig.h" +#import "ccMacros.h" +#import "CCConfiguration.h" +#import "Support/ccUtils.h" +#import "CCTexturePVR.h" + +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && CC_FONT_LABEL_SUPPORT +// FontLabel support +#import "FontManager.h" +#import "FontLabelStringDrawing.h" +#endif// CC_FONT_LABEL_SUPPORT + + +// For Labels use 32-bit textures on iPhone 3GS / iPads since A8 textures are very slow +#if defined(__ARM_NEON__) && CC_USE_RGBA32_LABELS_ON_NEON_ARCH +#define USE_TEXT_WITH_A8_TEXTURES 0 + +#else +#define USE_TEXT_WITH_A8_TEXTURES 1 +#endif + +//CLASS IMPLEMENTATIONS: + + +// If the image has alpha, you can create RGBA8 (32-bit) or RGBA4 (16-bit) or RGB5A1 (16-bit) +// Default is: RGBA8888 (32-bit textures) +static CCTexture2DPixelFormat defaultAlphaPixelFormat_ = kCCTexture2DPixelFormat_Default; + +#pragma mark - +#pragma mark CCTexture2D - Main + +@implementation CCTexture2D + +@synthesize contentSizeInPixels = size_, pixelFormat = format_, pixelsWide = width_, pixelsHigh = height_, name = name_, maxS = maxS_, maxT = maxT_; +@synthesize hasPremultipliedAlpha = hasPremultipliedAlpha_; + +- (id) initWithData:(const void*)data pixelFormat:(CCTexture2DPixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size +{ + if((self = [super init])) { + glGenTextures(1, &name_); + glBindTexture(GL_TEXTURE_2D, name_); + + [self setAntiAliasTexParameters]; + + // Specify OpenGL texture image + + switch(pixelFormat) + { + case kCCTexture2DPixelFormat_RGBA8888: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei) width, (GLsizei) height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + break; + case kCCTexture2DPixelFormat_RGBA4444: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei) width, (GLsizei) height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); + break; + case kCCTexture2DPixelFormat_RGB5A1: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei) width, (GLsizei) height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, data); + break; + case kCCTexture2DPixelFormat_RGB565: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (GLsizei) width, (GLsizei) height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); + break; + case kCCTexture2DPixelFormat_A8: + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, (GLsizei) width, (GLsizei) height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data); + break; + default: + [NSException raise:NSInternalInconsistencyException format:@""]; + + } + + size_ = size; + width_ = width; + height_ = height; + format_ = pixelFormat; + maxS_ = size.width / (float)width; + maxT_ = size.height / (float)height; + + hasPremultipliedAlpha_ = NO; + } + return self; +} + +- (void) releaseData:(void*)data +{ + //Free data + free(data); +} + +- (void*) keepData:(void*)data length:(NSUInteger)length +{ + //The texture data mustn't be saved becuase it isn't a mutable texture. + return data; +} + +- (void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + if(name_) + glDeleteTextures(1, &name_); + + [super dealloc]; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | Name = %i | Dimensions = %ix%i | Coordinates = (%.2f, %.2f)>", [self class], self, name_, width_, height_, maxS_, maxT_]; +} + +-(CGSize) contentSize +{ + CGSize ret; + ret.width = size_.width / CC_CONTENT_SCALE_FACTOR(); + ret.height = size_.height / CC_CONTENT_SCALE_FACTOR(); + + return ret; +} +@end + +#pragma mark - +#pragma mark CCTexture2D - Image + +@implementation CCTexture2D (Image) +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +- (id) initWithImage:(UIImage *)uiImage +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +- (id) initWithImage:(CGImageRef)CGImage +#endif +{ + NSUInteger POTWide, POTHigh; + CGContextRef context = nil; + void* data = nil;; + CGColorSpaceRef colorSpace; + void* tempData; + unsigned int* inPixel32; + unsigned short* outPixel16; + BOOL hasAlpha; + CGImageAlphaInfo info; + CGSize imageSize; + CCTexture2DPixelFormat pixelFormat; + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + CGImageRef CGImage = uiImage.CGImage; +#endif + + if(CGImage == NULL) { + CCLOG(@"cocos2d: CCTexture2D. Can't create Texture. UIImage is nil"); + [self release]; + return nil; + } + + CCConfiguration *conf = [CCConfiguration sharedConfiguration]; + +#if CC_TEXTURE_NPOT_SUPPORT + if( [conf supportsNPOT] ) { + POTWide = CGImageGetWidth(CGImage); + POTHigh = CGImageGetHeight(CGImage); + + } else +#endif + { + POTWide = ccNextPOT(CGImageGetWidth(CGImage)); + POTHigh = ccNextPOT(CGImageGetHeight(CGImage)); + } + + NSUInteger maxTextureSize = [conf maxTextureSize]; + if( POTHigh > maxTextureSize || POTWide > maxTextureSize ) { + CCLOG(@"cocos2d: WARNING: Image (%d x %d) is bigger than the supported %d x %d", + (unsigned int)POTWide, (unsigned int)POTHigh, + (unsigned int)maxTextureSize, (unsigned int)maxTextureSize); + [self release]; + return nil; + } + + info = CGImageGetAlphaInfo(CGImage); + hasAlpha = ((info == kCGImageAlphaPremultipliedLast) || (info == kCGImageAlphaPremultipliedFirst) || (info == kCGImageAlphaLast) || (info == kCGImageAlphaFirst) ? YES : NO); + + size_t bpp = CGImageGetBitsPerComponent(CGImage); + colorSpace = CGImageGetColorSpace(CGImage); + + if(colorSpace) { + if(hasAlpha || bpp >= 8) + pixelFormat = defaultAlphaPixelFormat_; + else { + CCLOG(@"cocos2d: CCTexture2D: Using RGB565 texture since image has no alpha"); + pixelFormat = kCCTexture2DPixelFormat_RGB565; + } + } else { + // NOTE: No colorspace means a mask image + CCLOG(@"cocos2d: CCTexture2D: Using A8 texture since image is a mask"); + pixelFormat = kCCTexture2DPixelFormat_A8; + } + + imageSize = CGSizeMake(CGImageGetWidth(CGImage), CGImageGetHeight(CGImage)); + + // Create the bitmap graphics context + + switch(pixelFormat) { + case kCCTexture2DPixelFormat_RGBA8888: + case kCCTexture2DPixelFormat_RGBA4444: + case kCCTexture2DPixelFormat_RGB5A1: + colorSpace = CGColorSpaceCreateDeviceRGB(); + data = malloc(POTHigh * POTWide * 4); + info = hasAlpha ? kCGImageAlphaPremultipliedLast : kCGImageAlphaNoneSkipLast; +// info = kCGImageAlphaPremultipliedLast; // issue #886. This patch breaks BMP images. + context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, 4 * POTWide, colorSpace, info | kCGBitmapByteOrder32Big); + CGColorSpaceRelease(colorSpace); + break; + + case kCCTexture2DPixelFormat_RGB565: + colorSpace = CGColorSpaceCreateDeviceRGB(); + data = malloc(POTHigh * POTWide * 4); + info = kCGImageAlphaNoneSkipLast; + context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, 4 * POTWide, colorSpace, info | kCGBitmapByteOrder32Big); + CGColorSpaceRelease(colorSpace); + break; + case kCCTexture2DPixelFormat_A8: + data = malloc(POTHigh * POTWide); + info = kCGImageAlphaOnly; + context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, POTWide, NULL, info); + break; + default: + [NSException raise:NSInternalInconsistencyException format:@"Invalid pixel format"]; + } + + + CGContextClearRect(context, CGRectMake(0, 0, POTWide, POTHigh)); + CGContextTranslateCTM(context, 0, POTHigh - imageSize.height); + CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(CGImage), CGImageGetHeight(CGImage)), CGImage); + + // Repack the pixel data into the right format + + if(pixelFormat == kCCTexture2DPixelFormat_RGB565) { + //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB" + tempData = malloc(POTHigh * POTWide * 2); + inPixel32 = (unsigned int*)data; + outPixel16 = (unsigned short*)tempData; + for(unsigned int i = 0; i < POTWide * POTHigh; ++i, ++inPixel32) + *outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | ((((*inPixel32 >> 8) & 0xFF) >> 2) << 5) | ((((*inPixel32 >> 16) & 0xFF) >> 3) << 0); + free(data); + data = tempData; + + } + else if (pixelFormat == kCCTexture2DPixelFormat_RGBA4444) { + //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRGGGGBBBBAAAA" + tempData = malloc(POTHigh * POTWide * 2); + inPixel32 = (unsigned int*)data; + outPixel16 = (unsigned short*)tempData; + for(unsigned int i = 0; i < POTWide * POTHigh; ++i, ++inPixel32) + *outPixel16++ = + ((((*inPixel32 >> 0) & 0xFF) >> 4) << 12) | // R + ((((*inPixel32 >> 8) & 0xFF) >> 4) << 8) | // G + ((((*inPixel32 >> 16) & 0xFF) >> 4) << 4) | // B + ((((*inPixel32 >> 24) & 0xFF) >> 4) << 0); // A + + + free(data); + data = tempData; + + } + else if (pixelFormat == kCCTexture2DPixelFormat_RGB5A1) { + //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGBBBBBA" + tempData = malloc(POTHigh * POTWide * 2); + inPixel32 = (unsigned int*)data; + outPixel16 = (unsigned short*)tempData; + for(unsigned int i = 0; i < POTWide * POTHigh; ++i, ++inPixel32) + *outPixel16++ = + ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | // R + ((((*inPixel32 >> 8) & 0xFF) >> 3) << 6) | // G + ((((*inPixel32 >> 16) & 0xFF) >> 3) << 1) | // B + ((((*inPixel32 >> 24) & 0xFF) >> 7) << 0); // A + + + free(data); + data = tempData; + } + self = [self initWithData:data pixelFormat:pixelFormat pixelsWide:POTWide pixelsHigh:POTHigh contentSize:imageSize]; + + // should be after calling super init + hasPremultipliedAlpha_ = (info == kCGImageAlphaPremultipliedLast || info == kCGImageAlphaPremultipliedFirst); + + CGContextRelease(context); + [self releaseData:data]; + + return self; +} +@end + +#pragma mark - +#pragma mark CCTexture2D - Text + +@implementation CCTexture2D (Text) + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment font:(id)uifont +{ + NSAssert( uifont, @"Invalid font"); + + NSUInteger POTWide = ccNextPOT(dimensions.width); + NSUInteger POTHigh = ccNextPOT(dimensions.height); + unsigned char* data; + + CGContextRef context; + CGColorSpaceRef colorSpace; + +#if USE_TEXT_WITH_A8_TEXTURES + colorSpace = CGColorSpaceCreateDeviceGray(); + data = calloc(POTHigh, POTWide); + context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, POTWide, colorSpace, kCGImageAlphaNone); +#else + colorSpace = CGColorSpaceCreateDeviceRGB(); + data = calloc(POTHigh, POTWide * 4); + context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, 4 * POTWide, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); +#endif + + CGColorSpaceRelease(colorSpace); + + if( ! context ) { + free(data); + [self release]; + return nil; + } + + CGContextSetGrayFillColor(context, 1.0f, 1.0f); + CGContextTranslateCTM(context, 0.0f, POTHigh); + CGContextScaleCTM(context, 1.0f, -1.0f); //NOTE: NSString draws in UIKit referential i.e. renders upside-down compared to CGBitmapContext referential + + UIGraphicsPushContext(context); + + // normal fonts + if( [uifont isKindOfClass:[UIFont class] ] ) + [string drawInRect:CGRectMake(0, 0, dimensions.width, dimensions.height) withFont:uifont lineBreakMode:UILineBreakModeWordWrap alignment:alignment]; + +#if CC_FONT_LABEL_SUPPORT + else // ZFont class + [string drawInRect:CGRectMake(0, 0, dimensions.width, dimensions.height) withZFont:uifont lineBreakMode:UILineBreakModeWordWrap alignment:alignment]; +#endif + + UIGraphicsPopContext(); + +#if USE_TEXT_WITH_A8_TEXTURES + self = [self initWithData:data pixelFormat:kCCTexture2DPixelFormat_A8 pixelsWide:POTWide pixelsHigh:POTHigh contentSize:dimensions]; +#else + self = [self initWithData:data pixelFormat:kCCTexture2DPixelFormat_RGBA8888 pixelsWide:POTWide pixelsHigh:POTHigh contentSize:dimensions]; +#endif + CGContextRelease(context); + [self releaseData:data]; + + return self; +} + +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment attributedString:(NSAttributedString*)stringWithAttributes +{ + NSAssert( stringWithAttributes, @"Invalid stringWithAttributes"); + + NSUInteger POTWide = ccNextPOT(dimensions.width); + NSUInteger POTHigh = ccNextPOT(dimensions.height); + unsigned char* data; + + NSSize realDimensions = [stringWithAttributes size]; + + //Alignment + float xPadding = 0; + + // Mac crashes if the width or height is 0 + if( realDimensions.width > 0 && realDimensions.height > 0 ) { + switch (alignment) { + case CCTextAlignmentLeft: xPadding = 0; break; + case CCTextAlignmentCenter: xPadding = (dimensions.width-realDimensions.width)/2.0f; break; + case CCTextAlignmentRight: xPadding = dimensions.width-realDimensions.width; break; + default: break; + } + + //Disable antialias + [[NSGraphicsContext currentContext] setShouldAntialias:NO]; + + NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(POTWide, POTHigh)]; + [image lockFocus]; + + [stringWithAttributes drawAtPoint:NSMakePoint(xPadding, POTHigh-dimensions.height)]; // draw at offset position + + NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect (0.0f, 0.0f, POTWide, POTHigh)]; + [image unlockFocus]; + + data = (unsigned char*) [bitmap bitmapData]; //Use the same buffer to improve the performance. + + NSUInteger textureSize = POTWide*POTHigh; + for(int i = 0; iwrapS == GL_CLAMP_TO_EDGE && texParams->wrapT == GL_CLAMP_TO_EDGE), + @"GL_CLAMP_TO_EDGE should be used in NPOT textures"); + glBindTexture( GL_TEXTURE_2D, self.name ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texParams->minFilter ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texParams->magFilter ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texParams->wrapS ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texParams->wrapT ); +} + +-(void) setAliasTexParameters +{ + ccTexParams texParams = { GL_NEAREST, GL_NEAREST, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE }; + [self setTexParameters: &texParams]; +} + +-(void) setAntiAliasTexParameters +{ + ccTexParams texParams = { GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE }; + [self setTexParameters: &texParams]; +} +@end + + +#pragma mark - +#pragma mark CCTexture2D - Pixel Format + +// +// Texture options for images that contains alpha +// +@implementation CCTexture2D (PixelFormat) ++(void) setDefaultAlphaPixelFormat:(CCTexture2DPixelFormat)format +{ + defaultAlphaPixelFormat_ = format; +} + ++(CCTexture2DPixelFormat) defaultAlphaPixelFormat +{ + return defaultAlphaPixelFormat_; +} +@end + diff --git a/libs/cocos2d/CCTextureAtlas.h b/libs/cocos2d/CCTextureAtlas.h new file mode 100644 index 0000000..e7d08b5 --- /dev/null +++ b/libs/cocos2d/CCTextureAtlas.h @@ -0,0 +1,146 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CCTexture2D.h" +#import "ccTypes.h" +#import "ccConfig.h" + +/** A class that implements a Texture Atlas. + Supported features: + * The atlas file can be a PVRTC, PNG or any other fomrat supported by Texture2D + * Quads can be udpated in runtime + * Quads can be added in runtime + * Quads can be removed in runtime + * Quads can be re-ordered in runtime + * The TextureAtlas capacity can be increased or decreased in runtime + * OpenGL component: V3F, C4B, T2F. + The quads are rendered using an OpenGL ES VBO. + To render the quads using an interleaved vertex array list, you should modify the ccConfig.h file + */ +@interface CCTextureAtlas : NSObject +{ + NSUInteger totalQuads_; + NSUInteger capacity_; + ccV3F_C4B_T2F_Quad *quads_; // quads to be rendered + GLushort *indices_; + CCTexture2D *texture_; +#if CC_USES_VBO + GLuint buffersVBO_[2]; //0: vertex 1: indices + BOOL dirty_; //indicates whether or not the array buffer of the VBO needs to be updated +#endif // CC_USES_VBO +} + +/** quantity of quads that are going to be drawn */ +@property (nonatomic,readonly) NSUInteger totalQuads; +/** quantity of quads that can be stored with the current texture atlas size */ +@property (nonatomic,readonly) NSUInteger capacity; +/** Texture of the texture atlas */ +@property (nonatomic,retain) CCTexture2D *texture; +/** Quads that are going to be rendered */ +@property (nonatomic,readwrite) ccV3F_C4B_T2F_Quad *quads; + +/** creates a TextureAtlas with an filename and with an initial capacity for Quads. + * The TextureAtlas capacity can be increased in runtime. + */ ++(id) textureAtlasWithFile:(NSString*)file capacity:(NSUInteger)capacity; + +/** initializes a TextureAtlas with a filename and with a certain capacity for Quads. + * The TextureAtlas capacity can be increased in runtime. + * + * WARNING: Do not reinitialize the TextureAtlas because it will leak memory (issue #706) + */ +-(id) initWithFile: (NSString*) file capacity:(NSUInteger)capacity; + +/** creates a TextureAtlas with a previously initialized Texture2D object, and + * with an initial capacity for n Quads. + * The TextureAtlas capacity can be increased in runtime. + */ ++(id) textureAtlasWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity; + +/** initializes a TextureAtlas with a previously initialized Texture2D object, and + * with an initial capacity for Quads. + * The TextureAtlas capacity can be increased in runtime. + * + * WARNING: Do not reinitialize the TextureAtlas because it will leak memory (issue #706) + */ +-(id) initWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity; + +/** updates a Quad (texture, vertex and color) at a certain index + * index must be between 0 and the atlas capacity - 1 + @since v0.8 + */ +-(void) updateQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger)index; + +/** Inserts a Quad (texture, vertex and color) at a certain index + index must be between 0 and the atlas capacity - 1 + @since v0.8 + */ +-(void) insertQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger)index; + +/** Removes the quad that is located at a certain index and inserts it at a new index + This operation is faster than removing and inserting in a quad in 2 different steps + @since v0.7.2 +*/ +-(void) insertQuadFromIndex:(NSUInteger)fromIndex atIndex:(NSUInteger)newIndex; + +/** removes a quad at a given index number. + The capacity remains the same, but the total number of quads to be drawn is reduced in 1 + @since v0.7.2 + */ +-(void) removeQuadAtIndex:(NSUInteger) index; + +/** removes all Quads. + The TextureAtlas capacity remains untouched. No memory is freed. + The total number of quads to be drawn will be 0 + @since v0.7.2 + */ +-(void) removeAllQuads; + +/** resize the capacity of the CCTextureAtlas. + * The new capacity can be lower or higher than the current one + * It returns YES if the resize was successful. + * If it fails to resize the capacity it will return NO with a new capacity of 0. + */ +-(BOOL) resizeCapacity: (NSUInteger) n; + + +/** draws n quads + * n can't be greater than the capacity of the Atlas + */ +-(void) drawNumberOfQuads: (NSUInteger) n; + + +/** draws n quads from an index (offset). + n + start can't be greater than the capacity of the atlas + + @since v1.0 + */ +-(void) drawNumberOfQuads: (NSUInteger) n fromIndex: (NSUInteger) start; + +/** draws all the Atlas's Quads + */ +-(void) drawQuads; + +@end diff --git a/libs/cocos2d/CCTextureAtlas.m b/libs/cocos2d/CCTextureAtlas.m new file mode 100644 index 0000000..bea248a --- /dev/null +++ b/libs/cocos2d/CCTextureAtlas.m @@ -0,0 +1,368 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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. + * + */ + + +// cocos2d +#import "CCTextureAtlas.h" +#import "ccMacros.h" +#import "CCTexture2D.h" +#import "CCTextureCache.h" + + +@interface CCTextureAtlas (Private) +-(void) initIndices; +@end + +//According to some tests GL_TRIANGLE_STRIP is slower, MUCH slower. Probably I'm doing something very wrong + +@implementation CCTextureAtlas + +@synthesize totalQuads = totalQuads_, capacity = capacity_; +@synthesize texture = texture_; +@synthesize quads = quads_; + +#pragma mark TextureAtlas - alloc & init + ++(id) textureAtlasWithFile:(NSString*) file capacity: (NSUInteger) n +{ + return [[[self alloc] initWithFile:file capacity:n] autorelease]; +} + ++(id) textureAtlasWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)n +{ + return [[[self alloc] initWithTexture:tex capacity:n] autorelease]; +} + +-(id) initWithFile:(NSString*)file capacity:(NSUInteger)n +{ + // retained in property + CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:file]; + if( tex ) + return [self initWithTexture:tex capacity:n]; + + // else + { + CCLOG(@"cocos2d: Could not open file: %@", file); + [self release]; + return nil; + } +} + +-(id) initWithTexture:(CCTexture2D*)tex capacity:(NSUInteger)n +{ + if( (self=[super init]) ) { + + capacity_ = n; + totalQuads_ = 0; + + // retained in property + self.texture = tex; + + // Re-initialization is not allowed + NSAssert(quads_==nil && indices_==nil, @"CCTextureAtlas re-initialization is not allowed"); + + quads_ = calloc( sizeof(quads_[0]) * capacity_, 1 ); + indices_ = calloc( sizeof(indices_[0]) * capacity_ * 6, 1 ); + + if( ! ( quads_ && indices_) ) { + CCLOG(@"cocos2d: CCTextureAtlas: not enough memory"); + if( quads_ ) + free(quads_); + if( indices_ ) + free(indices_); + return nil; + } + +#if CC_USES_VBO + // initial binding + glGenBuffers(2, &buffersVBO_[0]); + dirty_ = YES; +#endif // CC_USES_VBO + + [self initIndices]; + } + + return self; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | totalQuads = %i>", [self class], self, totalQuads_]; +} + +-(void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@",self); + + free(quads_); + free(indices_); + +#if CC_USES_VBO + glDeleteBuffers(2, buffersVBO_); +#endif // CC_USES_VBO + + + [texture_ release]; + + [super dealloc]; +} + +-(void) initIndices +{ + for( NSUInteger i=0;i< capacity_;i++) { +#if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP + indices_[i*6+0] = i*4+0; + indices_[i*6+1] = i*4+0; + indices_[i*6+2] = i*4+2; + indices_[i*6+3] = i*4+1; + indices_[i*6+4] = i*4+3; + indices_[i*6+5] = i*4+3; +#else + indices_[i*6+0] = i*4+0; + indices_[i*6+1] = i*4+1; + indices_[i*6+2] = i*4+2; + + // inverted index. issue #179 + indices_[i*6+3] = i*4+3; + indices_[i*6+4] = i*4+2; + indices_[i*6+5] = i*4+1; +// indices_[i*6+3] = i*4+2; +// indices_[i*6+4] = i*4+3; +// indices_[i*6+5] = i*4+1; +#endif + } + +#if CC_USES_VBO + glBindBuffer(GL_ARRAY_BUFFER, buffersVBO_[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0]) * capacity_, quads_, GL_DYNAMIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffersVBO_[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices_[0]) * capacity_ * 6, indices_, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +#endif // CC_USES_VBO +} + +#pragma mark TextureAtlas - Update, Insert, Move & Remove + +-(void) updateQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger) n +{ + NSAssert(n < capacity_, @"updateQuadWithTexture: Invalid index"); + + totalQuads_ = MAX( n+1, totalQuads_); + + quads_[n] = *quad; + +#if CC_USES_VBO + dirty_ = YES; +#endif +} + + +-(void) insertQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger)index +{ + NSAssert(index < capacity_, @"insertQuadWithTexture: Invalid index"); + + totalQuads_++; + NSAssert( totalQuads_ <= capacity_, @"invalid totalQuads"); + + // issue #575. index can be > totalQuads + NSInteger remaining = (totalQuads_-1) - index; + + // last object doesn't need to be moved + if( remaining > 0) + // tex coordinates + memmove( &quads_[index+1],&quads_[index], sizeof(quads_[0]) * remaining ); + + quads_[index] = *quad; + +#if CC_USES_VBO + dirty_ = YES; +#endif +} + + +-(void) insertQuadFromIndex:(NSUInteger)oldIndex atIndex:(NSUInteger)newIndex +{ + NSAssert(newIndex < totalQuads_, @"insertQuadFromIndex:atIndex: Invalid index"); + NSAssert(oldIndex < totalQuads_, @"insertQuadFromIndex:atIndex: Invalid index"); + + if( oldIndex == newIndex ) + return; + + NSUInteger howMany = labs( oldIndex - newIndex); + NSUInteger dst = oldIndex; + NSUInteger src = oldIndex + 1; + if( oldIndex > newIndex) { + dst = newIndex+1; + src = newIndex; + } + + // tex coordinates + ccV3F_C4B_T2F_Quad quadsBackup = quads_[oldIndex]; + memmove( &quads_[dst],&quads_[src], sizeof(quads_[0]) * howMany ); + quads_[newIndex] = quadsBackup; + +#if CC_USES_VBO + dirty_ = YES; +#endif +} + +-(void) removeQuadAtIndex:(NSUInteger) index +{ + NSAssert(index < totalQuads_, @"removeQuadAtIndex: Invalid index"); + + NSUInteger remaining = (totalQuads_-1) - index; + + + // last object doesn't need to be moved + if( remaining ) + // tex coordinates + memmove( &quads_[index],&quads_[index+1], sizeof(quads_[0]) * remaining ); + + totalQuads_--; + +#if CC_USES_VBO + dirty_ = YES; +#endif +} + +-(void) removeAllQuads +{ + totalQuads_ = 0; +} + +#pragma mark TextureAtlas - Resize + +-(BOOL) resizeCapacity: (NSUInteger) newCapacity +{ + if( newCapacity == capacity_ ) + return YES; + + // update capacity and totolQuads + totalQuads_ = MIN(totalQuads_,newCapacity); + capacity_ = newCapacity; + + void * tmpQuads = realloc( quads_, sizeof(quads_[0]) * capacity_ ); + void * tmpIndices = realloc( indices_, sizeof(indices_[0]) * capacity_ * 6 ); + + if( ! ( tmpQuads && tmpIndices) ) { + CCLOG(@"cocos2d: CCTextureAtlas: not enough memory"); + if( tmpQuads ) + free(tmpQuads); + else + free(quads_); + + if( tmpIndices ) + free(tmpIndices); + else + free(indices_); + + indices_ = nil; + quads_ = nil; + capacity_ = totalQuads_ = 0; + return NO; + } + + quads_ = tmpQuads; + indices_ = tmpIndices; + + [self initIndices]; + +#if CC_USES_VBO + dirty_ = YES; +#endif + return YES; +} + +#pragma mark TextureAtlas - Drawing + +-(void) drawQuads +{ + [self drawNumberOfQuads: totalQuads_ fromIndex:0]; +} + +-(void) drawNumberOfQuads: (NSUInteger) n +{ + [self drawNumberOfQuads:n fromIndex:0]; +} + +-(void) drawNumberOfQuads: (NSUInteger) n fromIndex: (NSUInteger) start +{ + // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Unneeded states: - + + glBindTexture(GL_TEXTURE_2D, [texture_ name]); +#define kQuadSize sizeof(quads_[0].bl) +#if CC_USES_VBO + glBindBuffer(GL_ARRAY_BUFFER, buffersVBO_[0]); + + // XXX: update is done in draw... perhaps it should be done in a timer + if (dirty_) { + glBufferSubData(GL_ARRAY_BUFFER, sizeof(quads_[0])*start, sizeof(quads_[0]) * n , &quads_[start] ); + dirty_ = NO; + } + + // vertices + glVertexPointer(3, GL_FLOAT, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices)); + + // colors + glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors)); + + // tex coords + glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords)); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffersVBO_[1]); +#if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP + glDrawElements(GL_TRIANGLE_STRIP, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) ); +#else + glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) ); +#endif // CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +#else // ! CC_USES_VBO + + NSUInteger offset = (NSUInteger)quads_; + // vertex + NSUInteger diff = offsetof( ccV3F_C4B_T2F, vertices); + glVertexPointer(3, GL_FLOAT, kQuadSize, (GLvoid*) (offset + diff) ); + // color + diff = offsetof( ccV3F_C4B_T2F, colors); + glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*)(offset + diff)); + + // tex coords + diff = offsetof( ccV3F_C4B_T2F, texCoords); + glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*)(offset + diff)); + +#if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP + glDrawElements(GL_TRIANGLE_STRIP, n*6, GL_UNSIGNED_SHORT, indices_ + start * 6 ); +#else + glDrawElements(GL_TRIANGLES, n*6, GL_UNSIGNED_SHORT, indices_ + start * 6 ); +#endif + +#endif // CC_USES_VBO +} +@end diff --git a/libs/cocos2d/CCTextureCache.h b/libs/cocos2d/CCTextureCache.h new file mode 100644 index 0000000..dbf138e --- /dev/null +++ b/libs/cocos2d/CCTextureCache.h @@ -0,0 +1,139 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import +#endif + +#import + +@class CCTexture2D; + +/** Singleton that handles the loading of textures + * Once the texture is loaded, the next time it will return + * a reference of the previously loaded texture reducing GPU & CPU memory + */ +@interface CCTextureCache : NSObject +{ + NSMutableDictionary *textures_; + NSLock *dictLock_; + NSLock *contextLock_; +} + +/** Retruns ths shared instance of the cache */ ++ (CCTextureCache *) sharedTextureCache; + +/** purges the cache. It releases the retained instance. + @since v0.99.0 + */ ++(void)purgeSharedTextureCache; + + +/** Returns a Texture2D object given an file image + * If the file image was not previously loaded, it will create a new CCTexture2D + * object and it will return it. It will use the filename as a key. + * Otherwise it will return a reference of a previosly loaded image. + * Supported image extensions: .png, .bmp, .tiff, .jpeg, .pvr, .gif + */ +-(CCTexture2D*) addImage: (NSString*) fileimage; + +/** Returns a Texture2D object given a file image + * If the file image was not previously loaded, it will create a new CCTexture2D object and it will return it. + * Otherwise it will load a texture in a new thread, and when the image is loaded, the callback will be called with the Texture2D as a parameter. + * The callback will be called from the main thread, so it is safe to create any cocos2d object from the callback. + * Supported image extensions: .png, .bmp, .tiff, .jpeg, .pvr, .gif + * @since v0.8 + */ +-(void) addImageAsync:(NSString*) filename target:(id)target selector:(SEL)selector; + +/** Returns a Texture2D object given an CGImageRef image + * If the image was not previously loaded, it will create a new CCTexture2D object and it will return it. + * Otherwise it will return a reference of a previously loaded image + * The "key" parameter will be used as the "key" for the cache. + * If "key" is nil, then a new texture will be created each time. + * @since v0.8 + */ +-(CCTexture2D*) addCGImage: (CGImageRef) image forKey: (NSString *)key; + +/** Returns an already created texture. Returns nil if the texture doesn't exist. + @since v0.99.5 + */ +-(CCTexture2D *) textureForKey:(NSString *)key; + +/** Purges the dictionary of loaded textures. + * Call this method if you receive the "Memory Warning" + * In the short term: it will free some resources preventing your app from being killed + * In the medium term: it will allocate more resources + * In the long term: it will be the same + */ +-(void) removeAllTextures; + +/** Removes unused textures + * Textures that have a retain count of 1 will be deleted + * It is convinient to call this method after when starting a new Scene + * @since v0.8 + */ +-(void) removeUnusedTextures; + +/** Deletes a texture from the cache given a texture + */ +-(void) removeTexture: (CCTexture2D*) tex; + +/** Deletes a texture from the cache given a its key name + @since v0.99.4 + */ +-(void) removeTextureForKey: (NSString*) textureKeyName; + +@end + + +@interface CCTextureCache (PVRSupport) + +/** Returns a Texture2D object given an PVRTC RAW filename + * If the file image was not previously loaded, it will create a new CCTexture2D + * object and it will return it. Otherwise it will return a reference of a previosly loaded image + * + * It can only load square images: width == height, and it must be a power of 2 (128,256,512...) + * bpp can only be 2 or 4. 2 means more compression but lower quality. + * hasAlpha: whether or not the image contains alpha channel + * + * IMPORTANT: This method is only defined on iOS. It is not supported on the Mac version. + */ +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +-(CCTexture2D*) addPVRTCImage:(NSString*)fileimage bpp:(int)bpp hasAlpha:(BOOL)alpha width:(int)w; +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED + +/** Returns a Texture2D object given an PVR filename. + * If the file image was not previously loaded, it will create a new CCTexture2D + * object and it will return it. Otherwise it will return a reference of a previosly loaded image + * + */ +-(CCTexture2D*) addPVRImage:(NSString*) filename; + +@end + + diff --git a/libs/cocos2d/CCTextureCache.m b/libs/cocos2d/CCTextureCache.m new file mode 100644 index 0000000..52a5678 --- /dev/null +++ b/libs/cocos2d/CCTextureCache.m @@ -0,0 +1,466 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 + +#import "Platforms/CCGL.h" +#import "CCTextureCache.h" +#import "CCTexture2D.h" +#import "CCTexturePVR.h" +#import "ccMacros.h" +#import "CCConfiguration.h" +#import "Support/CCFileUtils.h" +#import "CCDirector.h" +#import "ccConfig.h" + +// needed for CCCallFuncO in Mac-display_link version +#import "CCActionManager.h" +#import "CCActionInstant.h" + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +static EAGLContext *auxGLcontext = nil; +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +static NSOpenGLContext *auxGLcontext = nil; +#endif + + +@interface CCAsyncObject : NSObject +{ + SEL selector_; + id target_; + id data_; +} +@property (readwrite,assign) SEL selector; +@property (readwrite,retain) id target; +@property (readwrite,retain) id data; +@end + +@implementation CCAsyncObject +@synthesize selector = selector_; +@synthesize target = target_; +@synthesize data = data_; +- (void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + [target_ release]; + [data_ release]; + [super dealloc]; +} +@end + + +@implementation CCTextureCache + +#pragma mark TextureCache - Alloc, Init & Dealloc +static CCTextureCache *sharedTextureCache; + ++ (CCTextureCache *)sharedTextureCache +{ + if (!sharedTextureCache) + sharedTextureCache = [[CCTextureCache alloc] init]; + + return sharedTextureCache; +} + ++(id)alloc +{ + NSAssert(sharedTextureCache == nil, @"Attempted to allocate a second instance of a singleton."); + return [super alloc]; +} + ++(void)purgeSharedTextureCache +{ + [sharedTextureCache release]; + sharedTextureCache = nil; +} + +-(id) init +{ + if( (self=[super init]) ) { + textures_ = [[NSMutableDictionary dictionaryWithCapacity: 10] retain]; + dictLock_ = [[NSLock alloc] init]; + contextLock_ = [[NSLock alloc] init]; + } + + return self; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | num of textures = %i | keys: %@>", + [self class], + self, + [textures_ count], + [textures_ allKeys] + ]; + +} + +-(void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + + [textures_ release]; + [dictLock_ release]; + [contextLock_ release]; + [auxGLcontext release]; + auxGLcontext = nil; + sharedTextureCache = nil; + [super dealloc]; +} + +#pragma mark TextureCache - Add Images + +-(void) addImageWithAsyncObject:(CCAsyncObject*)async +{ + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + // textures will be created on the main OpenGL context + // it seems that in SDK 2.2.x there can't be 2 threads creating textures at the same time + // the lock is used for this purpose: issue #472 + [contextLock_ lock]; + if( auxGLcontext == nil ) { + auxGLcontext = [[EAGLContext alloc] + initWithAPI:kEAGLRenderingAPIOpenGLES1 + sharegroup:[[[[CCDirector sharedDirector] openGLView] context] sharegroup]]; + + if( ! auxGLcontext ) + CCLOG(@"cocos2d: TextureCache: Could not create EAGL context"); + } + + if( [EAGLContext setCurrentContext:auxGLcontext] ) { + + // load / create the texture + CCTexture2D *tex = [self addImage:async.data]; + + // The callback will be executed on the main thread + [async.target performSelectorOnMainThread:async.selector withObject:tex waitUntilDone:NO]; + + [EAGLContext setCurrentContext:nil]; + } else { + CCLOG(@"cocos2d: TetureCache: EAGLContext error"); + } + [contextLock_ unlock]; + + [autoreleasepool release]; + +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + + [contextLock_ lock]; + if( auxGLcontext == nil ) { + + MacGLView *view = [[CCDirector sharedDirector] openGLView]; + + NSOpenGLPixelFormat *pf = [view pixelFormat]; + NSOpenGLContext *share = [view openGLContext]; + + auxGLcontext = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:share]; + + if( ! auxGLcontext ) + CCLOG(@"cocos2d: TextureCache: Could not create NSOpenGLContext"); + } + + [auxGLcontext makeCurrentContext]; + + // load / create the texture + CCTexture2D *tex = [self addImage:async.data]; + +#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD + id action = [CCCallFuncO actionWithTarget:async.target selector:async.selector object:tex]; + [[CCActionManager sharedManager] addAction:action target:async.target paused:NO]; +#else + // The callback will be executed on the main thread + [async.target performSelector:async.selector + onThread:[[CCDirector sharedDirector] runningThread] + withObject:tex + waitUntilDone:NO]; +#endif + + + [NSOpenGLContext clearCurrentContext]; + + [contextLock_ unlock]; + + [autoreleasepool release]; + +#endif // __MAC_OS_X_VERSION_MAX_ALLOWED +} + +-(void) addImageAsync: (NSString*)path target:(id)target selector:(SEL)selector +{ + NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill"); + + // optimization + + CCTexture2D * tex; + + path = ccRemoveHDSuffixFromFile(path); + + if( (tex=[textures_ objectForKey: path] ) ) { + [target performSelector:selector withObject:tex]; + return; + } + + // schedule the load + + CCAsyncObject *asyncObject = [[CCAsyncObject alloc] init]; + asyncObject.selector = selector; + asyncObject.target = target; + asyncObject.data = path; + + [NSThread detachNewThreadSelector:@selector(addImageWithAsyncObject:) toTarget:self withObject:asyncObject]; + [asyncObject release]; +} + +-(CCTexture2D*) addImage: (NSString*) path +{ + NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill"); + + CCTexture2D * tex = nil; + + // MUTEX: + // Needed since addImageAsync calls this method from a different thread + [dictLock_ lock]; + + // remove possible -HD suffix to prevent caching the same image twice (issue #1040) + path = ccRemoveHDSuffixFromFile( path ); + + tex=[textures_ objectForKey: path]; + + if( ! tex ) { + + NSString *lowerCase = [path lowercaseString]; + // all images are handled by UIImage except PVR extension that is handled by our own handler + + if ( [lowerCase hasSuffix:@".pvr"] || [lowerCase hasSuffix:@".pvr.gz"] || [lowerCase hasSuffix:@".pvr.ccz"] ) + tex = [self addPVRImage:path]; + + // Only iPhone +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + + // Issue #886: TEMPORARY FIX FOR TRANSPARENT JPEGS IN IOS4 + else if ( ( [[CCConfiguration sharedConfiguration] OSVersion] >= kCCiOSVersion_4_0) && + ( [lowerCase hasSuffix:@".jpg"] || [lowerCase hasSuffix:@".jpeg"] ) + ) { + // convert jpg to png before loading the texture + + NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ]; + + UIImage *jpg = [[UIImage alloc] initWithContentsOfFile:fullpath]; + UIImage *png = [[UIImage alloc] initWithData:UIImagePNGRepresentation(jpg)]; + tex = [ [CCTexture2D alloc] initWithImage: png ]; + [png release]; + [jpg release]; + + if( tex ) + [textures_ setObject: tex forKey:path]; + else + CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path); + + [tex release]; + } + + else { + + // prevents overloading the autorelease pool + NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ]; + + UIImage *image = [ [UIImage alloc] initWithContentsOfFile: fullpath ]; + tex = [ [CCTexture2D alloc] initWithImage: image ]; + [image release]; + + if( tex ) + [textures_ setObject: tex forKey:path]; + else + CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path); + + [tex release]; + } + + // Only in Mac +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + else { + NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ]; + + NSData *data = [[NSData alloc] initWithContentsOfFile:fullpath]; + NSBitmapImageRep *image = [[NSBitmapImageRep alloc] initWithData:data]; + tex = [ [CCTexture2D alloc] initWithImage:[image CGImage]]; + + [data release]; + [image release]; + + if( tex ) + [textures_ setObject: tex forKey:path]; + else + CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path); + + [tex release]; + } +#endif // __MAC_OS_X_VERSION_MAX_ALLOWED + + } + + [dictLock_ unlock]; + + return tex; +} + + +-(CCTexture2D*) addCGImage: (CGImageRef) imageref forKey: (NSString *)key +{ + NSAssert(imageref != nil, @"TextureCache: image MUST not be nill"); + + CCTexture2D * tex = nil; + + // If key is nil, then create a new texture each time + if( key && (tex=[textures_ objectForKey: key] ) ) { + return tex; + } + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + // prevents overloading the autorelease pool + UIImage *image = [[UIImage alloc] initWithCGImage:imageref]; + tex = [[CCTexture2D alloc] initWithImage: image]; + [image release]; + +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + tex = [[CCTexture2D alloc] initWithImage: imageref]; +#endif + + if(tex && key) + [textures_ setObject: tex forKey:key]; + else + CCLOG(@"cocos2d: Couldn't add CGImage in CCTextureCache"); + + return [tex autorelease]; +} + +#pragma mark TextureCache - Remove + +-(void) removeAllTextures +{ + [textures_ removeAllObjects]; +} + +-(void) removeUnusedTextures +{ + NSArray *keys = [textures_ allKeys]; + for( id key in keys ) { + id value = [textures_ objectForKey:key]; + if( [value retainCount] == 1 ) { + CCLOG(@"cocos2d: CCTextureCache: removing unused texture: %@", key); + [textures_ removeObjectForKey:key]; + } + } +} + +-(void) removeTexture: (CCTexture2D*) tex +{ + if( ! tex ) + return; + + NSArray *keys = [textures_ allKeysForObject:tex]; + + for( NSUInteger i = 0; i < [keys count]; i++ ) + [textures_ removeObjectForKey:[keys objectAtIndex:i]]; +} + +-(void) removeTextureForKey:(NSString*)name +{ + if( ! name ) + return; + + [textures_ removeObjectForKey:name]; +} + +#pragma mark TextureCache - Get +- (CCTexture2D *)textureForKey:(NSString *)key +{ + return [textures_ objectForKey:key]; +} + +@end + + +@implementation CCTextureCache (PVRSupport) + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +-(CCTexture2D*) addPVRTCImage:(NSString*)path bpp:(int)bpp hasAlpha:(BOOL)alpha width:(int)w +{ + NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill"); + NSAssert( bpp==2 || bpp==4, @"TextureCache: bpp must be either 2 or 4"); + + CCTexture2D * tex; + + // remove possible -HD suffix to prevent caching the same image twice (issue #1040) + path = ccRemoveHDSuffixFromFile( path ); + + if( (tex=[textures_ objectForKey: path] ) ) { + return tex; + } + + // Split up directory and filename + NSString *fullpath = [CCFileUtils fullPathFromRelativePath:path]; + + NSData *nsdata = [[NSData alloc] initWithContentsOfFile:fullpath]; + tex = [[CCTexture2D alloc] initWithPVRTCData:[nsdata bytes] level:0 bpp:bpp hasAlpha:alpha length:w]; + if( tex ) + [textures_ setObject: tex forKey:path]; + else + CCLOG(@"cocos2d: Couldn't add PVRTCImage:%@ in CCTextureCache",path); + + [nsdata release]; + + return [tex autorelease]; +} +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED + +-(CCTexture2D*) addPVRImage:(NSString*)path +{ + NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill"); + + CCTexture2D * tex; + + // remove possible -HD suffix to prevent caching the same image twice (issue #1040) + path = ccRemoveHDSuffixFromFile( path ); + + if( (tex=[textures_ objectForKey: path] ) ) { + return tex; + } + + // Split up directory and filename + NSString *fullpath = [CCFileUtils fullPathFromRelativePath:path]; + + tex = [[CCTexture2D alloc] initWithPVRFile: fullpath]; + if( tex ) + [textures_ setObject: tex forKey:path]; + else + CCLOG(@"cocos2d: Couldn't add PVRImage:%@ in CCTextureCache",path); + + return [tex autorelease]; +} + +@end diff --git a/libs/cocos2d/CCTexturePVR.h b/libs/cocos2d/CCTexturePVR.h new file mode 100644 index 0000000..56249de --- /dev/null +++ b/libs/cocos2d/CCTexturePVR.h @@ -0,0 +1,125 @@ +/* + +File: PVRTexture.h +Abstract: The PVRTexture class is responsible for loading .pvr files. + +Version: 1.0 + +Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. +("Apple") in consideration of your agreement to the following terms, and your +use, installation, modification or redistribution of this Apple software +constitutes acceptance of these terms. If you do not agree with these terms, +please do not use, install, modify or redistribute this Apple software. + +In consideration of your agreement to abide by the following terms, and subject +to these terms, Apple grants you a personal, non-exclusive license, under +Apple's copyrights in this original Apple software (the "Apple Software"), to +use, reproduce, modify and redistribute the Apple Software, with or without +modifications, in source and/or binary forms; provided that if you redistribute +the Apple Software in its entirety and without modifications, you must retain +this notice and the following text and disclaimers in all such redistributions +of the Apple Software. +Neither the name, trademarks, service marks or logos of Apple Inc. may be used +to endorse or promote products derived from the Apple Software without specific +prior written permission from Apple. Except as expressly stated in this notice, +no other rights or licenses, express or implied, are granted by Apple herein, +including but not limited to any patent rights that may be infringed by your +derivative works or by other works in which the Apple Software may be +incorporated. + +The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO +WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED +WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN +COMBINATION WITH YOUR PRODUCTS. + +IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR +DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF +CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Copyright (C) 2008 Apple Inc. All Rights Reserved. + +*/ + +#import + +#import "Platforms/CCGL.h" +#import "CCTexture2D.h" + + +#pragma mark - +#pragma mark CCTexturePVR + +struct CCPVRMipmap { + unsigned char *address; + unsigned int len; +}; + +enum { + CC_PVRMIPMAP_MAX = 16, +}; + +/** CCTexturePVR + + Object that loads PVR images. + + Supported PVR formats: + - RGBA8888 + - BGRA8888 + - RGBA4444 + - RGBA5551 + - RGB565 + - A8 + - I8 + - AI88 + - PVRTC 4BPP + - PVRTC 2BPP + + Limitations: + Pre-generated mipmaps, such as PVR textures with mipmap levels embedded in file, + are only supported if all individual sprites are of _square_ size. + To use mipmaps with non-square textures, instead call CCTexture2D#generateMipmap on the sheet texture itself + (and to save space, save the PVR sprite sheet without mip maps included). + */ +@interface CCTexturePVR : NSObject +{ + struct CCPVRMipmap mipmaps_[CC_PVRMIPMAP_MAX]; // pointer to mipmap images + int numberOfMipmaps_; // number of mipmap used + + unsigned int tableFormatIndex_; + uint32_t width_, height_; + GLuint name_; + BOOL hasAlpha_; + + // cocos2d integration + BOOL retainName_; +} + +/** initializes a CCTexturePVR with a path */ +- (id)initWithContentsOfFile:(NSString *)path; +/** initializes a CCTexturePVR with an URL */ +- (id)initWithContentsOfURL:(NSURL *)url; +/** creates and initializes a CCTexturePVR with a path */ ++ (id)pvrTextureWithContentsOfFile:(NSString *)path; +/** creates and initializes a CCTexturePVR with an URL */ ++ (id)pvrTextureWithContentsOfURL:(NSURL *)url; + +/** texture id name */ +@property (nonatomic,readonly) GLuint name; +/** texture width */ +@property (nonatomic,readonly) uint32_t width; +/** texture height */ +@property (nonatomic,readonly) uint32_t height; +/** whether or not the texture has alpha */ +@property (nonatomic,readonly) BOOL hasAlpha; + +// cocos2d integration +@property (nonatomic,readwrite) BOOL retainName; + +@end + + diff --git a/libs/cocos2d/CCTexturePVR.m b/libs/cocos2d/CCTexturePVR.m new file mode 100644 index 0000000..781a74a --- /dev/null +++ b/libs/cocos2d/CCTexturePVR.m @@ -0,0 +1,406 @@ +/* + +File: PVRTexture.m +Abstract: The PVRTexture class is responsible for loading .pvr files. + +Version: 1.0 + +Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. +("Apple") in consideration of your agreement to the following terms, and your +use, installation, modification or redistribution of this Apple software +constitutes acceptance of these terms. If you do not agree with these terms, +please do not use, install, modify or redistribute this Apple software. + +In consideration of your agreement to abide by the following terms, and subject +to these terms, Apple grants you a personal, non-exclusive license, under +Apple's copyrights in this original Apple software (the "Apple Software"), to +use, reproduce, modify and redistribute the Apple Software, with or without +modifications, in source and/or binary forms; provided that if you redistribute +the Apple Software in its entirety and without modifications, you must retain +this notice and the following text and disclaimers in all such redistributions +of the Apple Software. +Neither the name, trademarks, service marks or logos of Apple Inc. may be used +to endorse or promote products derived from the Apple Software without specific +prior written permission from Apple. Except as expressly stated in this notice, +no other rights or licenses, express or implied, are granted by Apple herein, +including but not limited to any patent rights that may be infringed by your +derivative works or by other works in which the Apple Software may be +incorporated. + +The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO +WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED +WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN +COMBINATION WITH YOUR PRODUCTS. + +IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR +DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF +CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Copyright (C) 2008 Apple Inc. All Rights Reserved. + +*/ + +/* + * Extended PVR formats for cocos2d project ( http://www.cocos2d-iphone.org ) + * - RGBA8888 + * - BGRA8888 + * - RGBA4444 + * - RGBA5551 + * - RGB565 + * - A8 + * - I8 + * - AI88 + */ + +#import + +#import + +#import "CCTexturePVR.h" +#import "ccMacros.h" +#import "CCConfiguration.h" +#import "Support/ccUtils.h" +#import "Support/CCFileUtils.h" +#import "Support/ZipUtils.h" + +#pragma mark - +#pragma mark CCTexturePVR + +#define PVR_TEXTURE_FLAG_TYPE_MASK 0xff +#define PVR_TEXTURE_FLAG_FLIPPED_MASK 0x10000 + +static char gPVRTexIdentifier[4] = "PVR!"; + +enum +{ + kPVRTextureFlagTypeRGBA_4444= 0x10, + kPVRTextureFlagTypeRGBA_5551, + kPVRTextureFlagTypeRGBA_8888, + kPVRTextureFlagTypeRGB_565, + kPVRTextureFlagTypeRGB_555, // unsupported + kPVRTextureFlagTypeRGB_888, // unsupported + kPVRTextureFlagTypeI_8, + kPVRTextureFlagTypeAI_88, + kPVRTextureFlagTypePVRTC_2, + kPVRTextureFlagTypePVRTC_4, + kPVRTextureFlagTypeBGRA_8888, + kPVRTextureFlagTypeA_8, +}; + +static const uint32_t tableFormats[][6] = { + + // - PVR texture format + // - OpenGL internal format + // - OpenGL format + // - OpenGL type + // - bpp + // - compressed + { kPVRTextureFlagTypeRGBA_4444, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 16, NO }, + { kPVRTextureFlagTypeRGBA_5551, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 16, NO }, + { kPVRTextureFlagTypeRGBA_8888, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 32, NO }, + { kPVRTextureFlagTypeRGB_565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 16, NO }, + { kPVRTextureFlagTypeI_8, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 8, NO }, + { kPVRTextureFlagTypeAI_88, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 16, NO }, +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + { kPVRTextureFlagTypePVRTC_2, GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, -1, -1, 2, YES }, + { kPVRTextureFlagTypePVRTC_4, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, -1, -1, 4, YES }, +#endif // iphone only + { kPVRTextureFlagTypeBGRA_8888, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE, 32, NO }, + { kPVRTextureFlagTypeA_8, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, 8, NO }, +}; +#define MAX_TABLE_ELEMENTS (sizeof(tableFormats) / sizeof(tableFormats[0])) + +enum { + kCCInternalPVRTextureFormat, + kCCInternalOpenGLInternalFormat, + kCCInternalOpenGLFormat, + kCCInternalOpenGLType, + kCCInternalBPP, + kCCInternalCompressedImage, +}; + +typedef struct _PVRTexHeader +{ + uint32_t headerLength; + uint32_t height; + uint32_t width; + uint32_t numMipmaps; + uint32_t flags; + uint32_t dataLength; + uint32_t bpp; + uint32_t bitmaskRed; + uint32_t bitmaskGreen; + uint32_t bitmaskBlue; + uint32_t bitmaskAlpha; + uint32_t pvrTag; + uint32_t numSurfs; +} PVRTexHeader; + + +@implementation CCTexturePVR + +@synthesize name = name_; +@synthesize width = width_; +@synthesize height = height_; +@synthesize hasAlpha = hasAlpha_; + +// cocos2d integration +@synthesize retainName = retainName_; + + +- (BOOL)unpackPVRData:(unsigned char*)data PVRLen:(NSUInteger)len +{ + BOOL success = FALSE; + PVRTexHeader *header = NULL; + uint32_t flags, pvrTag; + uint32_t dataLength = 0, dataOffset = 0, dataSize = 0; + uint32_t blockSize = 0, widthBlocks = 0, heightBlocks = 0; + uint32_t width = 0, height = 0, bpp = 4; + uint8_t *bytes = NULL; + uint32_t formatFlags; + + header = (PVRTexHeader *)data; + + pvrTag = CFSwapInt32LittleToHost(header->pvrTag); + + if ((uint32_t)gPVRTexIdentifier[0] != ((pvrTag >> 0) & 0xff) || + (uint32_t)gPVRTexIdentifier[1] != ((pvrTag >> 8) & 0xff) || + (uint32_t)gPVRTexIdentifier[2] != ((pvrTag >> 16) & 0xff) || + (uint32_t)gPVRTexIdentifier[3] != ((pvrTag >> 24) & 0xff)) + { + return FALSE; + } + + flags = CFSwapInt32LittleToHost(header->flags); + formatFlags = flags & PVR_TEXTURE_FLAG_TYPE_MASK; + BOOL flipped = flags & PVR_TEXTURE_FLAG_FLIPPED_MASK; + if( flipped ) + CCLOG(@"cocos2d: WARNING: Image is flipped. Regenerate it using PVRTexTool"); + + if( header->width != ccNextPOT(header->width) || header->height != ccNextPOT(header->height) ) { + CCLOG(@"cocos2d: WARNING: PVR NPOT textures are not supported. Regenerate it."); + return FALSE; + } + + for( tableFormatIndex_=0; tableFormatIndex_ < (unsigned int)MAX_TABLE_ELEMENTS ; tableFormatIndex_++) { + if( tableFormats[tableFormatIndex_][kCCInternalPVRTextureFormat] == formatFlags ) { + + numberOfMipmaps_ = 0; + + width_ = width = CFSwapInt32LittleToHost(header->width); + height_ = height = CFSwapInt32LittleToHost(header->height); + + if (CFSwapInt32LittleToHost(header->bitmaskAlpha)) + hasAlpha_ = TRUE; + else + hasAlpha_ = FALSE; + + dataLength = CFSwapInt32LittleToHost(header->dataLength); + bytes = ((uint8_t *)data) + sizeof(PVRTexHeader); + + // Calculate the data size for each texture level and respect the minimum number of blocks + while (dataOffset < dataLength) + { + switch (formatFlags) { + case kPVRTextureFlagTypePVRTC_2: + blockSize = 8 * 4; // Pixel by pixel block size for 2bpp + widthBlocks = width / 8; + heightBlocks = height / 4; + bpp = 2; + break; + case kPVRTextureFlagTypePVRTC_4: + blockSize = 4 * 4; // Pixel by pixel block size for 4bpp + widthBlocks = width / 4; + heightBlocks = height / 4; + bpp = 4; + break; + case kPVRTextureFlagTypeBGRA_8888: + if( ! [[CCConfiguration sharedConfiguration] supportsBGRA8888] ) { + CCLOG(@"cocos2d: TexturePVR. BGRA8888 not supported on this device"); + return FALSE; + } + default: + blockSize = 1; + widthBlocks = width; + heightBlocks = height; + bpp = tableFormats[ tableFormatIndex_][ kCCInternalBPP]; + break; + } + + // Clamp to minimum number of blocks + if (widthBlocks < 2) + widthBlocks = 2; + if (heightBlocks < 2) + heightBlocks = 2; + + dataSize = widthBlocks * heightBlocks * ((blockSize * bpp) / 8); + float packetLenght = (dataLength-dataOffset); + packetLenght = packetLenght > dataSize ? dataSize : packetLenght; + + mipmaps_[numberOfMipmaps_].address = bytes+dataOffset; + mipmaps_[numberOfMipmaps_].len = packetLenght; + numberOfMipmaps_++; + + NSAssert( numberOfMipmaps_ < CC_PVRMIPMAP_MAX, @"TexturePVR: Maximum number of mimpaps reached. Increate the CC_PVRMIPMAP_MAX value"); + + dataOffset += packetLenght; + + width = MAX(width >> 1, 1); + height = MAX(height >> 1, 1); + } + + success = TRUE; + break; + } + } + + if( ! success ) + CCLOG(@"cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%2x. Re-encode it with a OpenGL pixel format variant", formatFlags); + + return success; +} + + +- (BOOL)createGLTexture +{ + GLsizei width = width_; + GLsizei height = height_; + GLenum err; + + if (numberOfMipmaps_ > 0) + { + if (name_ != 0) + glDeleteTextures(1, &name_); + + glGenTextures(1, &name_); + glBindTexture(GL_TEXTURE_2D, name_); + } + + // Generate textures with mipmaps + for (unsigned int i=0; i < numberOfMipmaps_; i++) + { + GLenum internalFormat = tableFormats[tableFormatIndex_][kCCInternalOpenGLInternalFormat]; + GLenum format = tableFormats[tableFormatIndex_][kCCInternalOpenGLFormat]; + GLenum type = tableFormats[tableFormatIndex_][kCCInternalOpenGLType]; + BOOL compressed = tableFormats[tableFormatIndex_][kCCInternalCompressedImage]; + + if( compressed && ! [[CCConfiguration sharedConfiguration] supportsPVRTC] ) { + CCLOG(@"cocos2d: WARNING: PVRTC images are not supported"); + return FALSE; + } + + unsigned char *data = mipmaps_[i].address; + unsigned int datalen = mipmaps_[i].len; + + if( compressed) + glCompressedTexImage2D(GL_TEXTURE_2D, i, internalFormat, width, height, 0, datalen, data); + else + glTexImage2D(GL_TEXTURE_2D, i, internalFormat, width, height, 0, format, type, data); + + if( i > 0 && (width != height || ccNextPOT(width) != width ) ) + CCLOG(@"cocos2d: TexturePVR. WARNING. Mipmap level %u is not squared. Texture won't render correctly. width=%u != height=%u", i, width, height); + + err = glGetError(); + if (err != GL_NO_ERROR) + { + CCLOG(@"cocos2d: TexturePVR: Error uploading compressed texture level: %u . glError: 0x%04X", (unsigned int)i, err); + return FALSE; + } + + width = MAX(width >> 1, 1); + height = MAX(height >> 1, 1); + } + + return TRUE; +} + + +- (id)initWithContentsOfFile:(NSString *)path +{ + if((self = [super init])) + { + unsigned char *pvrdata = NULL; + NSInteger pvrlen = 0; + NSString *lowerCase = [path lowercaseString]; + + if ( [lowerCase hasSuffix:@".ccz"]) + pvrlen = ccInflateCCZFile( [path UTF8String], &pvrdata ); + + else if( [lowerCase hasSuffix:@".gz"] ) + pvrlen = ccInflateGZipFile( [path UTF8String], &pvrdata ); + + else + pvrlen = ccLoadFileIntoMemory( [path UTF8String], &pvrdata ); + + if( pvrlen < 0 ) { + [self release]; + return nil; + } + + + numberOfMipmaps_ = 0; + + name_ = 0; + width_ = height_ = 0; + tableFormatIndex_ = -1; + hasAlpha_ = FALSE; + + retainName_ = NO; // cocos2d integration + + if( ! [self unpackPVRData:pvrdata PVRLen:pvrlen] || ![self createGLTexture] ) { + free(pvrdata); + [self release]; + return nil; + } + + free(pvrdata); + } + + return self; +} + +- (id)initWithContentsOfURL:(NSURL *)url +{ + if (![url isFileURL]) + { + CCLOG(@"cocos2d: CCPVRTexture: Only files are supported"); + [self release]; + return nil; + } + + return [self initWithContentsOfFile:[url path]]; +} + + ++ (id)pvrTextureWithContentsOfFile:(NSString *)path +{ + return [[[self alloc] initWithContentsOfFile:path] autorelease]; +} + + ++ (id)pvrTextureWithContentsOfURL:(NSURL *)url +{ + if (![url isFileURL]) + return nil; + + return [CCTexturePVR pvrTextureWithContentsOfFile:[url path]]; +} + + +- (void)dealloc +{ + CCLOGINFO( @"cocos2d: deallocing %@", self); + + if (name_ != 0 && ! retainName_ ) + glDeleteTextures(1, &name_); + + [super dealloc]; +} + +@end + diff --git a/libs/cocos2d/CCTileMapAtlas.h b/libs/cocos2d/CCTileMapAtlas.h new file mode 100644 index 0000000..e4b60e2 --- /dev/null +++ b/libs/cocos2d/CCTileMapAtlas.h @@ -0,0 +1,82 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CCTextureAtlas.h" +#import "CCAtlasNode.h" +#import "Support/TGAlib.h" + +/** CCTileMapAtlas is a subclass of CCAtlasNode. + + It knows how to render a map based of tiles. + The tiles must be in a .PNG format while the map must be a .TGA file. + + For more information regarding the format, please see this post: + http://www.cocos2d-iphone.org/archives/27 + + All features from CCAtlasNode are valid in CCTileMapAtlas + + IMPORTANT: + This class is deprecated. It is maintained for compatibility reasons only. + You SHOULD not use this class. + Instead, use the newer TMX file format: CCTMXTiledMap + */ +@interface CCTileMapAtlas : CCAtlasNode +{ + + /// info about the map file + tImageTGA *tgaInfo; + + /// x,y to altas dicctionary + NSMutableDictionary *posToAtlasIndex; + + /// numbers of tiles to render + int itemsToRender; +} + +/** TileMap info */ +@property (nonatomic,readonly) tImageTGA *tgaInfo; + +/** creates a CCTileMap with a tile file (atlas) with a map file and the width and height of each tile in points. + The tile file will be loaded using the TextureMgr. + */ ++(id) tileMapAtlasWithTileFile:(NSString*)tile mapFile:(NSString*)map tileWidth:(int)w tileHeight:(int)h; + +/** initializes a CCTileMap with a tile file (atlas) with a map file and the width and height of each tile in points. + The file will be loaded using the TextureMgr. + */ +-(id) initWithTileFile:(NSString*)tile mapFile:(NSString*)map tileWidth:(int)w tileHeight:(int)h; + +/** returns a tile from position x,y. + For the moment only channel R is used + */ +-(ccColor3B) tileAt: (ccGridSize) position; + +/** sets a tile at position x,y. + For the moment only channel R is used + */ +-(void) setTile:(ccColor3B)tile at:(ccGridSize)position; +/** dealloc the map from memory */ +-(void) releaseMap; +@end diff --git a/libs/cocos2d/CCTileMapAtlas.m b/libs/cocos2d/CCTileMapAtlas.m new file mode 100644 index 0000000..d57e1d9 --- /dev/null +++ b/libs/cocos2d/CCTileMapAtlas.m @@ -0,0 +1,233 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "ccConfig.h" +#import "CCTileMapAtlas.h" +#import "ccMacros.h" +#import "Support/CCFileUtils.h" + +@interface CCTileMapAtlas (Private) +-(void) loadTGAfile:(NSString*)file; +-(void) calculateItemsToRender; +-(void) updateAtlasValueAt:(ccGridSize)pos withValue:(ccColor3B)value withIndex:(NSUInteger)idx; +@end + + +@implementation CCTileMapAtlas + +@synthesize tgaInfo; + +#pragma mark CCTileMapAtlas - Creation & Init ++(id) tileMapAtlasWithTileFile:(NSString*)tile mapFile:(NSString*)map tileWidth:(int)w tileHeight:(int)h +{ + return [[[self alloc] initWithTileFile:tile mapFile:map tileWidth:w tileHeight:h] autorelease]; +} + + +-(id) initWithTileFile:(NSString*)tile mapFile:(NSString*)map tileWidth:(int)w tileHeight:(int)h +{ + [self loadTGAfile: map]; + [self calculateItemsToRender]; + + if( (self=[super initWithTileFile:tile tileWidth:w tileHeight:h itemsToRender: itemsToRender]) ) { + + posToAtlasIndex = [[NSMutableDictionary dictionaryWithCapacity:itemsToRender] retain]; + + [self updateAtlasValues]; + + [self setContentSize: CGSizeMake(tgaInfo->width*itemWidth_, tgaInfo->height*itemHeight_)]; + } + + return self; +} + +-(void) dealloc +{ + if( tgaInfo ) + tgaDestroy(tgaInfo); + + [posToAtlasIndex release]; + + [super dealloc]; +} + +-(void) releaseMap +{ + if( tgaInfo ) + tgaDestroy(tgaInfo); + + tgaInfo = nil; + + [posToAtlasIndex release]; + posToAtlasIndex = nil; +} + +-(void) calculateItemsToRender +{ + NSAssert( tgaInfo != nil, @"tgaInfo must be non-nil"); + + itemsToRender = 0; + for(int x = 0;x < tgaInfo->width; x++ ) { + for(int y = 0; y < tgaInfo->height; y++ ) { + ccColor3B *ptr = (ccColor3B*) tgaInfo->imageData; + ccColor3B value = ptr[x + y * tgaInfo->width]; + if( value.r ) + itemsToRender++; + } + } +} + +-(void) loadTGAfile:(NSString*)file +{ + NSAssert( file != nil, @"file must be non-nil"); + + NSString *path = [CCFileUtils fullPathFromRelativePath:file ]; + +// //Find the path of the file +// NSBundle *mainBndl = [CCDirector sharedDirector].loadingBundle; +// NSString *resourcePath = [mainBndl resourcePath]; +// NSString * path = [resourcePath stringByAppendingPathComponent:file]; + + tgaInfo = tgaLoad( [path UTF8String] ); +#if 1 + if( tgaInfo->status != TGA_OK ) + [NSException raise:@"TileMapAtlasLoadTGA" format:@"TileMapAtas cannot load TGA file"]; + +#endif +} + +#pragma mark CCTileMapAtlas - Atlas generation / updates + +-(void) setTile:(ccColor3B) tile at:(ccGridSize) pos +{ + NSAssert( tgaInfo != nil, @"tgaInfo must not be nil"); + NSAssert( posToAtlasIndex != nil, @"posToAtlasIndex must not be nil"); + NSAssert( pos.x < tgaInfo->width, @"Invalid position.x"); + NSAssert( pos.y < tgaInfo->height, @"Invalid position.x"); + NSAssert( tile.r != 0, @"R component must be non 0"); + + ccColor3B *ptr = (ccColor3B*) tgaInfo->imageData; + ccColor3B value = ptr[pos.x + pos.y * tgaInfo->width]; + if( value.r == 0 ) + CCLOG(@"cocos2d: Value.r must be non 0."); + else { + ptr[pos.x + pos.y * tgaInfo->width] = tile; + + // XXX: this method consumes a lot of memory + // XXX: a tree of something like that shall be impolemented + NSNumber *num = [posToAtlasIndex objectForKey: [NSString stringWithFormat:@"%d,%d", pos.x, pos.y]]; + [self updateAtlasValueAt:pos withValue:tile withIndex: [num integerValue]]; + } +} + +-(ccColor3B) tileAt:(ccGridSize) pos +{ + NSAssert( tgaInfo != nil, @"tgaInfo must not be nil"); + NSAssert( pos.x < tgaInfo->width, @"Invalid position.x"); + NSAssert( pos.y < tgaInfo->height, @"Invalid position.y"); + + ccColor3B *ptr = (ccColor3B*) tgaInfo->imageData; + ccColor3B value = ptr[pos.x + pos.y * tgaInfo->width]; + + return value; +} + +-(void) updateAtlasValueAt:(ccGridSize)pos withValue:(ccColor3B)value withIndex:(NSUInteger)idx +{ + ccV3F_C4B_T2F_Quad quad; + + NSInteger x = pos.x; + NSInteger y = pos.y; + float row = (value.r % itemsPerRow_); + float col = (value.r / itemsPerRow_); + + float textureWide = [[textureAtlas_ texture] pixelsWide]; + float textureHigh = [[textureAtlas_ texture] pixelsHigh]; + +#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL + float left = (2*row*itemWidth_+1)/(2*textureWide); + float right = left+(itemWidth_*2-2)/(2*textureWide); + float top = (2*col*itemHeight_+1)/(2*textureHigh); + float bottom = top+(itemHeight_*2-2)/(2*textureHigh); +#else + float left = (row*itemWidth_)/textureWide; + float right = left+itemWidth_/textureWide; + float top = (col*itemHeight_)/textureHigh; + float bottom = top+itemHeight_/textureHigh; +#endif + + + quad.tl.texCoords.u = left; + quad.tl.texCoords.v = top; + quad.tr.texCoords.u = right; + quad.tr.texCoords.v = top; + quad.bl.texCoords.u = left; + quad.bl.texCoords.v = bottom; + quad.br.texCoords.u = right; + quad.br.texCoords.v = bottom; + + quad.bl.vertices.x = (int) (x * itemWidth_); + quad.bl.vertices.y = (int) (y * itemHeight_); + quad.bl.vertices.z = 0.0f; + quad.br.vertices.x = (int)(x * itemWidth_ + itemWidth_); + quad.br.vertices.y = (int)(y * itemHeight_); + quad.br.vertices.z = 0.0f; + quad.tl.vertices.x = (int)(x * itemWidth_); + quad.tl.vertices.y = (int)(y * itemHeight_ + itemHeight_); + quad.tl.vertices.z = 0.0f; + quad.tr.vertices.x = (int)(x * itemWidth_ + itemWidth_); + quad.tr.vertices.y = (int)(y * itemHeight_ + itemHeight_); + quad.tr.vertices.z = 0.0f; + + [textureAtlas_ updateQuad:&quad atIndex:idx]; +} + +-(void) updateAtlasValues +{ + NSAssert( tgaInfo != nil, @"tgaInfo must be non-nil"); + + + int total = 0; + + for(int x = 0;x < tgaInfo->width; x++ ) { + for(int y = 0; y < tgaInfo->height; y++ ) { + if( total < itemsToRender ) { + ccColor3B *ptr = (ccColor3B*) tgaInfo->imageData; + ccColor3B value = ptr[x + y * tgaInfo->width]; + + if( value.r != 0 ) { + [self updateAtlasValueAt:ccg(x,y) withValue:value withIndex:total]; + + NSString *key = [NSString stringWithFormat:@"%d,%d", x,y]; + NSNumber *num = [NSNumber numberWithInt:total]; + [posToAtlasIndex setObject:num forKey:key]; + + total++; + } + } + } + } +} +@end diff --git a/libs/cocos2d/CCTransition.h b/libs/cocos2d/CCTransition.h new file mode 100644 index 0000000..619a8a3 --- /dev/null +++ b/libs/cocos2d/CCTransition.h @@ -0,0 +1,295 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CCScene.h" +@class CCActionInterval; +@class CCNode; + +/** CCTransitionEaseScene can ease the actions of the scene protocol. + @since v0.8.2 + */ +@protocol CCTransitionEaseScene +/** returns the Ease action that will be performed on a linear action. + @since v0.8.2 + */ +-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action; +@end + +/** Orientation Type used by some transitions + */ +typedef enum { + /// An horizontal orientation where the Left is nearer + kOrientationLeftOver = 0, + /// An horizontal orientation where the Right is nearer + kOrientationRightOver = 1, + /// A vertical orientation where the Up is nearer + kOrientationUpOver = 0, + /// A vertical orientation where the Bottom is nearer + kOrientationDownOver = 1, +} tOrientation; + +/** Base class for CCTransition scenes + */ +@interface CCTransitionScene : CCScene +{ + CCScene *inScene_; + CCScene *outScene_; + ccTime duration_; + BOOL inSceneOnTop_; + BOOL sendCleanupToScene_; +} +/** creates a base transition with duration and incoming scene */ ++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s; +/** initializes a transition with duration and incoming scene */ +-(id) initWithDuration:(ccTime) t scene:(CCScene*)s; +/** called after the transition finishes */ +-(void) finish; +/** used by some transitions to hide the outter scene */ +-(void) hideOutShowIn; +@end + +/** A CCTransition that supports orientation like. + * Possible orientation: LeftOver, RightOver, UpOver, DownOver + */ +@interface CCTransitionSceneOriented : CCTransitionScene +{ + tOrientation orientation; +} +/** creates a base transition with duration and incoming scene */ ++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s orientation:(tOrientation)o; +/** initializes a transition with duration and incoming scene */ +-(id) initWithDuration:(ccTime) t scene:(CCScene*)s orientation:(tOrientation)o; +@end + + +/** CCTransitionRotoZoom: + Rotate and zoom out the outgoing scene, and then rotate and zoom in the incoming + */ +@interface CCTransitionRotoZoom : CCTransitionScene +{} +@end + +/** CCTransitionJumpZoom: + Zoom out and jump the outgoing scene, and then jump and zoom in the incoming +*/ +@interface CCTransitionJumpZoom : CCTransitionScene +{} +@end + +/** CCTransitionMoveInL: + Move in from to the left the incoming scene. +*/ +@interface CCTransitionMoveInL : CCTransitionScene +{} +/** initializes the scenes */ +-(void) initScenes; +/** returns the action that will be performed */ +-(CCActionInterval*) action; +@end + +/** CCTransitionMoveInR: + Move in from to the right the incoming scene. + */ +@interface CCTransitionMoveInR : CCTransitionMoveInL +{} +@end + +/** CCTransitionMoveInT: + Move in from to the top the incoming scene. + */ +@interface CCTransitionMoveInT : CCTransitionMoveInL +{} +@end + +/** CCTransitionMoveInB: + Move in from to the bottom the incoming scene. + */ +@interface CCTransitionMoveInB : CCTransitionMoveInL +{} +@end + +/** CCTransitionSlideInL: + Slide in the incoming scene from the left border. + */ +@interface CCTransitionSlideInL : CCTransitionScene +{} +/** initializes the scenes */ +-(void) initScenes; +/** returns the action that will be performed by the incomming and outgoing scene */ +-(CCActionInterval*) action; +@end + +/** CCTransitionSlideInR: + Slide in the incoming scene from the right border. + */ +@interface CCTransitionSlideInR : CCTransitionSlideInL +{} +@end + +/** CCTransitionSlideInB: + Slide in the incoming scene from the bottom border. + */ +@interface CCTransitionSlideInB : CCTransitionSlideInL +{} +@end + +/** CCTransitionSlideInT: + Slide in the incoming scene from the top border. + */ +@interface CCTransitionSlideInT : CCTransitionSlideInL +{} +@end + +/** + Shrink the outgoing scene while grow the incoming scene + */ +@interface CCTransitionShrinkGrow : CCTransitionScene +{} +@end + +/** CCTransitionFlipX: + Flips the screen horizontally. + The front face is the outgoing scene and the back face is the incoming scene. + */ +@interface CCTransitionFlipX : CCTransitionSceneOriented +{} +@end + +/** CCTransitionFlipY: + Flips the screen vertically. + The front face is the outgoing scene and the back face is the incoming scene. + */ +@interface CCTransitionFlipY : CCTransitionSceneOriented +{} +@end + +/** CCTransitionFlipAngular: + Flips the screen half horizontally and half vertically. + The front face is the outgoing scene and the back face is the incoming scene. + */ +@interface CCTransitionFlipAngular : CCTransitionSceneOriented +{} +@end + +/** CCTransitionZoomFlipX: + Flips the screen horizontally doing a zoom out/in + The front face is the outgoing scene and the back face is the incoming scene. + */ +@interface CCTransitionZoomFlipX : CCTransitionSceneOriented +{} +@end + +/** CCTransitionZoomFlipY: + Flips the screen vertically doing a little zooming out/in + The front face is the outgoing scene and the back face is the incoming scene. + */ +@interface CCTransitionZoomFlipY : CCTransitionSceneOriented +{} +@end + +/** CCTransitionZoomFlipAngular: + Flips the screen half horizontally and half vertically doing a little zooming out/in. + The front face is the outgoing scene and the back face is the incoming scene. + */ +@interface CCTransitionZoomFlipAngular : CCTransitionSceneOriented +{} +@end + +/** CCTransitionFade: + Fade out the outgoing scene and then fade in the incoming scene.''' + */ +@interface CCTransitionFade : CCTransitionScene +{ + ccColor4B color; +} +/** creates the transition with a duration and with an RGB color + * Example: [FadeTransition transitionWithDuration:2 scene:s withColor:ccc3(255,0,0)]; // red color + */ ++(id) transitionWithDuration:(ccTime)duration scene:(CCScene*)scene withColor:(ccColor3B)color; +/** initializes the transition with a duration and with an RGB color */ +-(id) initWithDuration:(ccTime)duration scene:(CCScene*)scene withColor:(ccColor3B)color; +@end + + +/** + CCTransitionCrossFade: + Cross fades two scenes using the CCRenderTexture object. + */ +@class CCRenderTexture; +@interface CCTransitionCrossFade : CCTransitionScene +{} +@end + +/** CCTransitionTurnOffTiles: + Turn off the tiles of the outgoing scene in random order + */ +@interface CCTransitionTurnOffTiles : CCTransitionScene +{} +@end + +/** CCTransitionSplitCols: + The odd columns goes upwards while the even columns goes downwards. + */ +@interface CCTransitionSplitCols : CCTransitionScene +{} +-(CCActionInterval*) action; +@end + +/** CCTransitionSplitRows: + The odd rows goes to the left while the even rows goes to the right. + */ +@interface CCTransitionSplitRows : CCTransitionSplitCols +{} +@end + +/** CCTransitionFadeTR: + Fade the tiles of the outgoing scene from the left-bottom corner the to top-right corner. + */ +@interface CCTransitionFadeTR : CCTransitionScene +{} +-(CCActionInterval*) actionWithSize:(ccGridSize) vector; +@end + +/** CCTransitionFadeBL: + Fade the tiles of the outgoing scene from the top-right corner to the bottom-left corner. + */ +@interface CCTransitionFadeBL : CCTransitionFadeTR +{} +@end + +/** CCTransitionFadeUp: + * Fade the tiles of the outgoing scene from the bottom to the top. + */ +@interface CCTransitionFadeUp : CCTransitionFadeTR +{} +@end + +/** CCTransitionFadeDown: + * Fade the tiles of the outgoing scene from the top to the bottom. + */ +@interface CCTransitionFadeDown : CCTransitionFadeTR +{} +@end diff --git a/libs/cocos2d/CCTransition.m b/libs/cocos2d/CCTransition.m new file mode 100644 index 0000000..61f96b7 --- /dev/null +++ b/libs/cocos2d/CCTransition.m @@ -0,0 +1,1057 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CCTransition.h" +#import "CCNode.h" +#import "CCDirector.h" +#import "CCActionInterval.h" +#import "CCActionInstant.h" +#import "CCActionCamera.h" +#import "CCLayer.h" +#import "CCCamera.h" +#import "CCActionTiledGrid.h" +#import "CCActionEase.h" +#import "CCRenderTexture.h" +#import "Support/CGPointExtension.h" + +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "Platforms/iOS/CCTouchDispatcher.h" +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import "Platforms/Mac/CCEventDispatcher.h" +#endif + +enum { + kSceneFade = 0xFADEFADE, +}; + +@interface CCTransitionScene (Private) +-(void) sceneOrder; +- (void)setNewScene:(ccTime)dt; +@end + +@implementation CCTransitionScene ++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s +{ + return [[[self alloc] initWithDuration:t scene:s] autorelease]; +} + +-(id) initWithDuration:(ccTime) t scene:(CCScene*)s +{ + NSAssert( s != nil, @"Argument scene must be non-nil"); + + if( (self=[super init]) ) { + + duration_ = t; + + // retain + inScene_ = [s retain]; + outScene_ = [[CCDirector sharedDirector] runningScene]; + [outScene_ retain]; + + NSAssert( inScene_ != outScene_, @"Incoming scene must be different from the outgoing scene" ); + + // disable events while transitions +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + [[CCTouchDispatcher sharedDispatcher] setDispatchEvents: NO]; +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + [[CCEventDispatcher sharedDispatcher] setDispatchEvents: NO]; +#endif + + [self sceneOrder]; + } + return self; +} +-(void) sceneOrder +{ + inSceneOnTop_ = YES; +} + +-(void) draw +{ + if( inSceneOnTop_ ) { + [outScene_ visit]; + [inScene_ visit]; + } else { + [inScene_ visit]; + [outScene_ visit]; + } +} + +-(void) finish +{ + /* clean up */ + [inScene_ setVisible:YES]; + [inScene_ setPosition:ccp(0,0)]; + [inScene_ setScale:1.0f]; + [inScene_ setRotation:0.0f]; + [inScene_.camera restore]; + + [outScene_ setVisible:NO]; + [outScene_ setPosition:ccp(0,0)]; + [outScene_ setScale:1.0f]; + [outScene_ setRotation:0.0f]; + [outScene_.camera restore]; + + [self schedule:@selector(setNewScene:) interval:0]; +} + +-(void) setNewScene: (ccTime) dt +{ + [self unschedule:_cmd]; + + CCDirector *director = [CCDirector sharedDirector]; + + // Before replacing, save the "send cleanup to scene" + sendCleanupToScene_ = [director sendCleanupToScene]; + + [director replaceScene: inScene_]; + + // enable events while transitions +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + [[CCTouchDispatcher sharedDispatcher] setDispatchEvents: YES]; +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + [[CCEventDispatcher sharedDispatcher] setDispatchEvents: YES]; +#endif + + // issue #267 + [outScene_ setVisible:YES]; +} + +-(void) hideOutShowIn +{ + [inScene_ setVisible:YES]; + [outScene_ setVisible:NO]; +} + +// custom onEnter +-(void) onEnter +{ + [super onEnter]; + [inScene_ onEnter]; + // outScene_ should not receive the onEnter callback +} + +// custom onExit +-(void) onExit +{ + [super onExit]; + [outScene_ onExit]; + + // inScene_ should not receive the onExit callback + // only the onEnterTransitionDidFinish + [inScene_ onEnterTransitionDidFinish]; +} + +// custom cleanup +-(void) cleanup +{ + [super cleanup]; + + if( sendCleanupToScene_ ) + [outScene_ cleanup]; +} + +-(void) dealloc +{ + [inScene_ release]; + [outScene_ release]; + [super dealloc]; +} +@end + +// +// Oriented Transition +// +@implementation CCTransitionSceneOriented ++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s orientation:(tOrientation)o +{ + return [[[self alloc] initWithDuration:t scene:s orientation:o] autorelease]; +} + +-(id) initWithDuration:(ccTime) t scene:(CCScene*)s orientation:(tOrientation)o +{ + if( (self=[super initWithDuration:t scene:s]) ) + orientation = o; + return self; +} +@end + + +// +// RotoZoom +// +@implementation CCTransitionRotoZoom +-(void) onEnter +{ + [super onEnter]; + + [inScene_ setScale:0.001f]; + [outScene_ setScale:1.0f]; + + [inScene_ setAnchorPoint:ccp(0.5f, 0.5f)]; + [outScene_ setAnchorPoint:ccp(0.5f, 0.5f)]; + + CCActionInterval *rotozoom = [CCSequence actions: [CCSpawn actions: + [CCScaleBy actionWithDuration:duration_/2 scale:0.001f], + [CCRotateBy actionWithDuration:duration_/2 angle:360 *2], + nil], + [CCDelayTime actionWithDuration:duration_/2], + nil]; + + + [outScene_ runAction: rotozoom]; + [inScene_ runAction: [CCSequence actions: + [rotozoom reverse], + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + nil]]; +} +@end + +// +// JumpZoom +// +@implementation CCTransitionJumpZoom +-(void) onEnter +{ + [super onEnter]; + CGSize s = [[CCDirector sharedDirector] winSize]; + + [inScene_ setScale:0.5f]; + [inScene_ setPosition:ccp( s.width,0 )]; + + [inScene_ setAnchorPoint:ccp(0.5f, 0.5f)]; + [outScene_ setAnchorPoint:ccp(0.5f, 0.5f)]; + + CCActionInterval *jump = [CCJumpBy actionWithDuration:duration_/4 position:ccp(-s.width,0) height:s.width/4 jumps:2]; + CCActionInterval *scaleIn = [CCScaleTo actionWithDuration:duration_/4 scale:1.0f]; + CCActionInterval *scaleOut = [CCScaleTo actionWithDuration:duration_/4 scale:0.5f]; + + CCActionInterval *jumpZoomOut = [CCSequence actions: scaleOut, jump, nil]; + CCActionInterval *jumpZoomIn = [CCSequence actions: jump, scaleIn, nil]; + + CCActionInterval *delay = [CCDelayTime actionWithDuration:duration_/2]; + + [outScene_ runAction: jumpZoomOut]; + [inScene_ runAction: [CCSequence actions: delay, + jumpZoomIn, + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + nil] ]; +} +@end + +// +// MoveInL +// +@implementation CCTransitionMoveInL +-(void) onEnter +{ + [super onEnter]; + + [self initScenes]; + + CCActionInterval *a = [self action]; + + [inScene_ runAction: [CCSequence actions: + [self easeActionWithAction:a], + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + nil] + ]; + +} +-(CCActionInterval*) action +{ + return [CCMoveTo actionWithDuration:duration_ position:ccp(0,0)]; +} + +-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action +{ + return [CCEaseOut actionWithAction:action rate:2.0f]; +// return [EaseElasticOut actionWithAction:action period:0.4f]; +} + +-(void) initScenes +{ + CGSize s = [[CCDirector sharedDirector] winSize]; + [inScene_ setPosition: ccp( -s.width,0) ]; +} +@end + +// +// MoveInR +// +@implementation CCTransitionMoveInR +-(void) initScenes +{ + CGSize s = [[CCDirector sharedDirector] winSize]; + [inScene_ setPosition: ccp( s.width,0) ]; +} +@end + +// +// MoveInT +// +@implementation CCTransitionMoveInT +-(void) initScenes +{ + CGSize s = [[CCDirector sharedDirector] winSize]; + [inScene_ setPosition: ccp( 0, s.height) ]; +} +@end + +// +// MoveInB +// +@implementation CCTransitionMoveInB +-(void) initScenes +{ + CGSize s = [[CCDirector sharedDirector] winSize]; + [inScene_ setPosition: ccp( 0, -s.height) ]; +} +@end + +// +// SlideInL +// + +// The adjust factor is needed to prevent issue #442 +// One solution is to use DONT_RENDER_IN_SUBPIXELS images, but NO +// The other issue is that in some transitions (and I don't know why) +// the order should be reversed (In in top of Out or vice-versa). +#define ADJUST_FACTOR 0.5f +@implementation CCTransitionSlideInL +-(void) onEnter +{ + [super onEnter]; + + [self initScenes]; + + CCActionInterval *in = [self action]; + CCActionInterval *out = [self action]; + + id inAction = [self easeActionWithAction:in]; + id outAction = [CCSequence actions: + [self easeActionWithAction:out], + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + nil]; + + [inScene_ runAction: inAction]; + [outScene_ runAction: outAction]; +} +-(void) sceneOrder +{ + inSceneOnTop_ = NO; +} +-(void) initScenes +{ + CGSize s = [[CCDirector sharedDirector] winSize]; + [inScene_ setPosition: ccp( -(s.width-ADJUST_FACTOR),0) ]; +} +-(CCActionInterval*) action +{ + CGSize s = [[CCDirector sharedDirector] winSize]; + return [CCMoveBy actionWithDuration:duration_ position:ccp(s.width-ADJUST_FACTOR,0)]; +} + +-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action +{ + return [CCEaseOut actionWithAction:action rate:2.0f]; +// return [EaseElasticOut actionWithAction:action period:0.4f]; +} + +@end + +// +// SlideInR +// +@implementation CCTransitionSlideInR +-(void) sceneOrder +{ + inSceneOnTop_ = YES; +} +-(void) initScenes +{ + CGSize s = [[CCDirector sharedDirector] winSize]; + [inScene_ setPosition: ccp( s.width-ADJUST_FACTOR,0) ]; +} + +-(CCActionInterval*) action +{ + CGSize s = [[CCDirector sharedDirector] winSize]; + return [CCMoveBy actionWithDuration:duration_ position:ccp(-(s.width-ADJUST_FACTOR),0)]; +} + +@end + +// +// SlideInT +// +@implementation CCTransitionSlideInT +-(void) sceneOrder +{ + inSceneOnTop_ = NO; +} +-(void) initScenes +{ + CGSize s = [[CCDirector sharedDirector] winSize]; + [inScene_ setPosition: ccp(0,s.height-ADJUST_FACTOR) ]; +} + +-(CCActionInterval*) action +{ + CGSize s = [[CCDirector sharedDirector] winSize]; + return [CCMoveBy actionWithDuration:duration_ position:ccp(0,-(s.height-ADJUST_FACTOR))]; +} + +@end + +// +// SlideInB +// +@implementation CCTransitionSlideInB +-(void) sceneOrder +{ + inSceneOnTop_ = YES; +} + +-(void) initScenes +{ + CGSize s = [[CCDirector sharedDirector] winSize]; + [inScene_ setPosition: ccp(0,-(s.height-ADJUST_FACTOR)) ]; +} + +-(CCActionInterval*) action +{ + CGSize s = [[CCDirector sharedDirector] winSize]; + return [CCMoveBy actionWithDuration:duration_ position:ccp(0,s.height-ADJUST_FACTOR)]; +} +@end + +// +// ShrinkGrow Transition +// +@implementation CCTransitionShrinkGrow +-(void) onEnter +{ + [super onEnter]; + + [inScene_ setScale:0.001f]; + [outScene_ setScale:1.0f]; + + [inScene_ setAnchorPoint:ccp(2/3.0f,0.5f)]; + [outScene_ setAnchorPoint:ccp(1/3.0f,0.5f)]; + + CCActionInterval *scaleOut = [CCScaleTo actionWithDuration:duration_ scale:0.01f]; + CCActionInterval *scaleIn = [CCScaleTo actionWithDuration:duration_ scale:1.0f]; + + [inScene_ runAction: [self easeActionWithAction:scaleIn]]; + [outScene_ runAction: [CCSequence actions: + [self easeActionWithAction:scaleOut], + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + nil] ]; +} +-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action +{ + return [CCEaseOut actionWithAction:action rate:2.0f]; +// return [EaseElasticOut actionWithAction:action period:0.3f]; +} +@end + +// +// FlipX Transition +// +@implementation CCTransitionFlipX +-(void) onEnter +{ + [super onEnter]; + + CCActionInterval *inA, *outA; + [inScene_ setVisible: NO]; + + float inDeltaZ, inAngleZ; + float outDeltaZ, outAngleZ; + + if( orientation == kOrientationRightOver ) { + inDeltaZ = 90; + inAngleZ = 270; + outDeltaZ = 90; + outAngleZ = 0; + } else { + inDeltaZ = -90; + inAngleZ = 90; + outDeltaZ = -90; + outAngleZ = 0; + } + + inA = [CCSequence actions: + [CCDelayTime actionWithDuration:duration_/2], + [CCShow action], + [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:0 deltaAngleX:0], + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + outA = [CCSequence actions: + [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:0 deltaAngleX:0], + [CCHide action], + [CCDelayTime actionWithDuration:duration_/2], + nil ]; + + [inScene_ runAction: inA]; + [outScene_ runAction: outA]; + +} +@end + +// +// FlipY Transition +// +@implementation CCTransitionFlipY +-(void) onEnter +{ + [super onEnter]; + + CCActionInterval *inA, *outA; + [inScene_ setVisible: NO]; + + float inDeltaZ, inAngleZ; + float outDeltaZ, outAngleZ; + + if( orientation == kOrientationUpOver ) { + inDeltaZ = 90; + inAngleZ = 270; + outDeltaZ = 90; + outAngleZ = 0; + } else { + inDeltaZ = -90; + inAngleZ = 90; + outDeltaZ = -90; + outAngleZ = 0; + } + inA = [CCSequence actions: + [CCDelayTime actionWithDuration:duration_/2], + [CCShow action], + [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:90 deltaAngleX:0], + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + outA = [CCSequence actions: + [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:90 deltaAngleX:0], + [CCHide action], + [CCDelayTime actionWithDuration:duration_/2], + nil ]; + + [inScene_ runAction: inA]; + [outScene_ runAction: outA]; + +} +@end + +// +// FlipAngular Transition +// +@implementation CCTransitionFlipAngular +-(void) onEnter +{ + [super onEnter]; + + CCActionInterval *inA, *outA; + [inScene_ setVisible: NO]; + + float inDeltaZ, inAngleZ; + float outDeltaZ, outAngleZ; + + if( orientation == kOrientationRightOver ) { + inDeltaZ = 90; + inAngleZ = 270; + outDeltaZ = 90; + outAngleZ = 0; + } else { + inDeltaZ = -90; + inAngleZ = 90; + outDeltaZ = -90; + outAngleZ = 0; + } + inA = [CCSequence actions: + [CCDelayTime actionWithDuration:duration_/2], + [CCShow action], + [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:-45 deltaAngleX:0], + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + outA = [CCSequence actions: + [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:45 deltaAngleX:0], + [CCHide action], + [CCDelayTime actionWithDuration:duration_/2], + nil ]; + + [inScene_ runAction: inA]; + [outScene_ runAction: outA]; +} +@end + +// +// ZoomFlipX Transition +// +@implementation CCTransitionZoomFlipX +-(void) onEnter +{ + [super onEnter]; + + CCActionInterval *inA, *outA; + [inScene_ setVisible: NO]; + + float inDeltaZ, inAngleZ; + float outDeltaZ, outAngleZ; + + if( orientation == kOrientationRightOver ) { + inDeltaZ = 90; + inAngleZ = 270; + outDeltaZ = 90; + outAngleZ = 0; + } else { + inDeltaZ = -90; + inAngleZ = 90; + outDeltaZ = -90; + outAngleZ = 0; + } + inA = [CCSequence actions: + [CCDelayTime actionWithDuration:duration_/2], + [CCSpawn actions: + [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:0 deltaAngleX:0], + [CCScaleTo actionWithDuration:duration_/2 scale:1], + [CCShow action], + nil], + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + outA = [CCSequence actions: + [CCSpawn actions: + [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:0 deltaAngleX:0], + [CCScaleTo actionWithDuration:duration_/2 scale:0.5f], + nil], + [CCHide action], + [CCDelayTime actionWithDuration:duration_/2], + nil ]; + + inScene_.scale = 0.5f; + [inScene_ runAction: inA]; + [outScene_ runAction: outA]; +} +@end + +// +// ZoomFlipY Transition +// +@implementation CCTransitionZoomFlipY +-(void) onEnter +{ + [super onEnter]; + + CCActionInterval *inA, *outA; + [inScene_ setVisible: NO]; + + float inDeltaZ, inAngleZ; + float outDeltaZ, outAngleZ; + + if( orientation == kOrientationUpOver ) { + inDeltaZ = 90; + inAngleZ = 270; + outDeltaZ = 90; + outAngleZ = 0; + } else { + inDeltaZ = -90; + inAngleZ = 90; + outDeltaZ = -90; + outAngleZ = 0; + } + + inA = [CCSequence actions: + [CCDelayTime actionWithDuration:duration_/2], + [CCSpawn actions: + [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:90 deltaAngleX:0], + [CCScaleTo actionWithDuration:duration_/2 scale:1], + [CCShow action], + nil], + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + outA = [CCSequence actions: + [CCSpawn actions: + [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:90 deltaAngleX:0], + [CCScaleTo actionWithDuration:duration_/2 scale:0.5f], + nil], + [CCHide action], + [CCDelayTime actionWithDuration:duration_/2], + nil ]; + + inScene_.scale = 0.5f; + [inScene_ runAction: inA]; + [outScene_ runAction: outA]; +} +@end + +// +// ZoomFlipAngular Transition +// +@implementation CCTransitionZoomFlipAngular +-(void) onEnter +{ + [super onEnter]; + + CCActionInterval *inA, *outA; + [inScene_ setVisible: NO]; + + float inDeltaZ, inAngleZ; + float outDeltaZ, outAngleZ; + + if( orientation == kOrientationRightOver ) { + inDeltaZ = 90; + inAngleZ = 270; + outDeltaZ = 90; + outAngleZ = 0; + } else { + inDeltaZ = -90; + inAngleZ = 90; + outDeltaZ = -90; + outAngleZ = 0; + } + + inA = [CCSequence actions: + [CCDelayTime actionWithDuration:duration_/2], + [CCSpawn actions: + [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:-45 deltaAngleX:0], + [CCScaleTo actionWithDuration:duration_/2 scale:1], + [CCShow action], + nil], + [CCShow action], + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + outA = [CCSequence actions: + [CCSpawn actions: + [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:45 deltaAngleX:0], + [CCScaleTo actionWithDuration:duration_/2 scale:0.5f], + nil], + [CCHide action], + [CCDelayTime actionWithDuration:duration_/2], + nil ]; + + inScene_.scale = 0.5f; + [inScene_ runAction: inA]; + [outScene_ runAction: outA]; +} +@end + + +// +// Fade Transition +// +@implementation CCTransitionFade ++(id) transitionWithDuration:(ccTime)d scene:(CCScene*)s withColor:(ccColor3B)color +{ + return [[[self alloc] initWithDuration:d scene:s withColor:color] autorelease]; +} + +-(id) initWithDuration:(ccTime)d scene:(CCScene*)s withColor:(ccColor3B)aColor +{ + if( (self=[super initWithDuration:d scene:s]) ) { + color.r = aColor.r; + color.g = aColor.g; + color.b = aColor.b; + } + + return self; +} + +-(id) initWithDuration:(ccTime)d scene:(CCScene*)s +{ + return [self initWithDuration:d scene:s withColor:ccBLACK]; +} + +-(void) onEnter +{ + [super onEnter]; + + CCLayerColor *l = [CCLayerColor layerWithColor:color]; + [inScene_ setVisible: NO]; + + [self addChild: l z:2 tag:kSceneFade]; + + + CCNode *f = [self getChildByTag:kSceneFade]; + + CCActionInterval *a = [CCSequence actions: + [CCFadeIn actionWithDuration:duration_/2], + [CCCallFunc actionWithTarget:self selector:@selector(hideOutShowIn)], + [CCFadeOut actionWithDuration:duration_/2], + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + [f runAction: a]; +} + +-(void) onExit +{ + [super onExit]; + [self removeChildByTag:kSceneFade cleanup:NO]; +} +@end + + +// +// Cross Fade Transition +// +@implementation CCTransitionCrossFade + +-(void) draw +{ + // override draw since both scenes (textures) are rendered in 1 scene +} + +-(void) onEnter +{ + [super onEnter]; + + // create a transparent color layer + // in which we are going to add our rendertextures + ccColor4B color = {0,0,0,0}; + CGSize size = [[CCDirector sharedDirector] winSize]; + CCLayerColor * layer = [CCLayerColor layerWithColor:color]; + + // create the first render texture for inScene_ + CCRenderTexture *inTexture = [CCRenderTexture renderTextureWithWidth:size.width height:size.height]; + inTexture.sprite.anchorPoint= ccp(0.5f,0.5f); + inTexture.position = ccp(size.width/2, size.height/2); + inTexture.anchorPoint = ccp(0.5f,0.5f); + + // render inScene_ to its texturebuffer + [inTexture begin]; + [inScene_ visit]; + [inTexture end]; + + // create the second render texture for outScene_ + CCRenderTexture *outTexture = [CCRenderTexture renderTextureWithWidth:size.width height:size.height]; + outTexture.sprite.anchorPoint= ccp(0.5f,0.5f); + outTexture.position = ccp(size.width/2, size.height/2); + outTexture.anchorPoint = ccp(0.5f,0.5f); + + // render outScene_ to its texturebuffer + [outTexture begin]; + [outScene_ visit]; + [outTexture end]; + + // create blend functions + + ccBlendFunc blend1 = {GL_ONE, GL_ONE}; // inScene_ will lay on background and will not be used with alpha + ccBlendFunc blend2 = {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA}; // we are going to blend outScene_ via alpha + + // set blendfunctions + [inTexture.sprite setBlendFunc:blend1]; + [outTexture.sprite setBlendFunc:blend2]; + + // add render textures to the layer + [layer addChild:inTexture]; + [layer addChild:outTexture]; + + // initial opacity: + [inTexture.sprite setOpacity:255]; + [outTexture.sprite setOpacity:255]; + + // create the blend action + CCActionInterval * layerAction = [CCSequence actions: + [CCFadeTo actionWithDuration:duration_ opacity:0], + [CCCallFunc actionWithTarget:self selector:@selector(hideOutShowIn)], + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + + + // run the blend action + [outTexture.sprite runAction: layerAction]; + + // add the layer (which contains our two rendertextures) to the scene + [self addChild: layer z:2 tag:kSceneFade]; +} + +// clean up on exit +-(void) onExit +{ + // remove our layer and release all containing objects + [self removeChildByTag:kSceneFade cleanup:NO]; + + [super onExit]; +} +@end + +// +// TurnOffTilesTransition +// +@implementation CCTransitionTurnOffTiles + +// override addScenes, and change the order +-(void) sceneOrder +{ + inSceneOnTop_ = NO; +} + +-(void) onEnter +{ + [super onEnter]; + CGSize s = [[CCDirector sharedDirector] winSize]; + float aspect = s.width / s.height; + int x = 12 * aspect; + int y = 12; + + id toff = [CCTurnOffTiles actionWithSize: ccg(x,y) duration:duration_]; + id action = [self easeActionWithAction:toff]; + [outScene_ runAction: [CCSequence actions: action, + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + [CCStopGrid action], + nil] + ]; + +} +-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action +{ + return action; +// return [EaseIn actionWithAction:action rate:2.0f]; +} +@end + +#pragma mark Split Transitions + +// +// SplitCols Transition +// +@implementation CCTransitionSplitCols + +-(void) onEnter +{ + [super onEnter]; + + inScene_.visible = NO; + + id split = [self action]; + id seq = [CCSequence actions: + split, + [CCCallFunc actionWithTarget:self selector:@selector(hideOutShowIn)], + [split reverse], + nil + ]; + [self runAction: [CCSequence actions: + [self easeActionWithAction:seq], + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + [CCStopGrid action], + nil] + ]; +} + +-(CCActionInterval*) action +{ + return [CCSplitCols actionWithCols:3 duration:duration_/2.0f]; +} + +-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action +{ + return [CCEaseInOut actionWithAction:action rate:3.0f]; +} +@end + +// +// SplitRows Transition +// +@implementation CCTransitionSplitRows +-(CCActionInterval*) action +{ + return [CCSplitRows actionWithRows:3 duration:duration_/2.0f]; +} +@end + + +#pragma mark Fade Grid Transitions + +// +// FadeTR Transition +// +@implementation CCTransitionFadeTR +-(void) sceneOrder +{ + inSceneOnTop_ = NO; +} + +-(void) onEnter +{ + [super onEnter]; + + CGSize s = [[CCDirector sharedDirector] winSize]; + float aspect = s.width / s.height; + int x = 12 * aspect; + int y = 12; + + id action = [self actionWithSize:ccg(x,y)]; + + [outScene_ runAction: [CCSequence actions: + [self easeActionWithAction:action], + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + [CCStopGrid action], + nil] + ]; +} + +-(CCActionInterval*) actionWithSize: (ccGridSize) v +{ + return [CCFadeOutTRTiles actionWithSize:v duration:duration_]; +} + +-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action +{ + return action; +// return [EaseIn actionWithAction:action rate:2.0f]; +} +@end + +// +// FadeBL Transition +// +@implementation CCTransitionFadeBL +-(CCActionInterval*) actionWithSize: (ccGridSize) v +{ + return [CCFadeOutBLTiles actionWithSize:v duration:duration_]; +} +@end + +// +// FadeUp Transition +// +@implementation CCTransitionFadeUp +-(CCActionInterval*) actionWithSize: (ccGridSize) v +{ + return [CCFadeOutUpTiles actionWithSize:v duration:duration_]; +} +@end + +// +// FadeDown Transition +// +@implementation CCTransitionFadeDown +-(CCActionInterval*) actionWithSize: (ccGridSize) v +{ + return [CCFadeOutDownTiles actionWithSize:v duration:duration_]; +} +@end diff --git a/libs/cocos2d/CCTransitionPageTurn.h b/libs/cocos2d/CCTransitionPageTurn.h new file mode 100644 index 0000000..aacb7fc --- /dev/null +++ b/libs/cocos2d/CCTransitionPageTurn.h @@ -0,0 +1,60 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Sindesso Pty Ltd http://www.sindesso.com/ + * + * 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 "CCTransition.h" + +/** CCTransitionPageTurn transition. + * A transition which peels back the bottom right hand corner of a scene + * to transition to the scene beneath it simulating a page turn + * + * This uses a 3DAction so it's strongly recommended that depth buffering + * is turned on in CCDirector using: + * + * [[CCDirector sharedDirector] setDepthBufferFormat:kCCDepthBuffer16]; + * + * @since v0.8.2 + */ +@interface CCTransitionPageTurn : CCTransitionScene +{ + BOOL back_; +} +/** + * creates a base transition with duration and incoming scene + * if back is TRUE then the effect is reversed to appear as if the incoming + * scene is being turned from left over the outgoing scene + */ ++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s backwards:(BOOL) back; + +/** + * creates a base transition with duration and incoming scene + * if back is TRUE then the effect is reversed to appear as if the incoming + * scene is being turned from left over the outgoing scene + */ +-(id) initWithDuration:(ccTime) t scene:(CCScene*)s backwards:(BOOL) back; + +-(CCActionInterval*) actionWithSize:(ccGridSize) vector; + +@end diff --git a/libs/cocos2d/CCTransitionPageTurn.m b/libs/cocos2d/CCTransitionPageTurn.m new file mode 100644 index 0000000..bff43a7 --- /dev/null +++ b/libs/cocos2d/CCTransitionPageTurn.m @@ -0,0 +1,117 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Sindesso Pty Ltd http://www.sindesso.com/ + * + * 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 "CCTransitionPageTurn.h" +#import "CCActionPageTurn3D.h" +#import "CCDirector.h" + +@implementation CCTransitionPageTurn + +/** creates a base transition with duration and incoming scene */ ++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s backwards:(BOOL) back +{ + return [[[self alloc] initWithDuration:t scene:s backwards:back] autorelease]; +} + +/** initializes a transition with duration and incoming scene */ +-(id) initWithDuration:(ccTime) t scene:(CCScene*)s backwards:(BOOL) back +{ + // XXX: needed before [super init] + back_ = back; + + if( ( self = [super initWithDuration:t scene:s] ) ) + { + // do something + } + return self; +} + +-(void) sceneOrder +{ + inSceneOnTop_ = back_; +} + +// +-(void) onEnter +{ + [super onEnter]; + + CGSize s = [[CCDirector sharedDirector] winSize]; + int x, y; + if( s.width > s.height) + { + x = 16; + y = 12; + } + else + { + x = 12; + y = 16; + } + + id action = [self actionWithSize:ccg(x,y)]; + + if(! back_ ) + { + [outScene_ runAction: [CCSequence actions: + action, + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + [CCStopGrid action], + nil] + ]; + } + else + { + // to prevent initial flicker + inScene_.visible = NO; + [inScene_ runAction: [CCSequence actions: + [CCShow action], + action, + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + [CCStopGrid action], + nil] + ]; + } + +} + +-(CCActionInterval*) actionWithSize: (ccGridSize) v +{ + if( back_ ) + { + // Get hold of the PageTurn3DAction + return [CCReverseTime actionWithAction: + [CCPageTurn3D actionWithSize:v duration:duration_]]; + } + else + { + // Get hold of the PageTurn3DAction + return [CCPageTurn3D actionWithSize:v duration:duration_]; + } +} + +@end + diff --git a/libs/cocos2d/CCTransitionRadial.h b/libs/cocos2d/CCTransitionRadial.h new file mode 100644 index 0000000..6d4a5e0 --- /dev/null +++ b/libs/cocos2d/CCTransitionRadial.h @@ -0,0 +1,40 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Lam Pham + * + * 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 "CCTransition.h" +#import "CCProgressTimer.h" +#import "CCActionProgressTimer.h" + +/** CCTransitionRadialCCW transition. + A counter colock-wise radial transition to the next scene + */ +@interface CCTransitionRadialCCW : CCTransitionScene +@end + +/** CCTransitionRadialCW transition. + A counter colock-wise radial transition to the next scene +*/ +@interface CCTransitionRadialCW : CCTransitionRadialCCW +@end diff --git a/libs/cocos2d/CCTransitionRadial.m b/libs/cocos2d/CCTransitionRadial.m new file mode 100644 index 0000000..a892f35 --- /dev/null +++ b/libs/cocos2d/CCTransitionRadial.m @@ -0,0 +1,115 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Lam Pham + * + * 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 "CCDirector.h" +#import "CCTransitionRadial.h" +#import "CCRenderTexture.h" +#import "CCLayer.h" +#import "CCActionInstant.h" +#import "Support/CGPointExtension.h" + +enum { + kSceneRadial = 0xc001, +}; + +#pragma mark - +#pragma mark Transition Radial CCW + +@implementation CCTransitionRadialCCW +-(void) sceneOrder +{ + inSceneOnTop_ = NO; +} + +-(CCProgressTimerType) radialType +{ + return kCCProgressTimerTypeRadialCCW; +} + +-(void) onEnter +{ + [super onEnter]; + // create a transparent color layer + // in which we are going to add our rendertextures + CGSize size = [[CCDirector sharedDirector] winSize]; + + // create the second render texture for outScene + CCRenderTexture *outTexture = [CCRenderTexture renderTextureWithWidth:size.width height:size.height]; + outTexture.sprite.anchorPoint= ccp(0.5f,0.5f); + outTexture.position = ccp(size.width/2, size.height/2); + outTexture.anchorPoint = ccp(0.5f,0.5f); + + // render outScene to its texturebuffer + [outTexture clear:0 g:0 b:0 a:1]; + [outTexture begin]; + [outScene_ visit]; + [outTexture end]; + + // Since we've passed the outScene to the texture we don't need it. + [self hideOutShowIn]; + + // We need the texture in RenderTexture. + CCProgressTimer *outNode = [CCProgressTimer progressWithTexture:outTexture.sprite.texture]; + // but it's flipped upside down so we flip the sprite + outNode.sprite.flipY = YES; + // Return the radial type that we want to use + outNode.type = [self radialType]; + outNode.percentage = 100.f; + outNode.position = ccp(size.width/2, size.height/2); + outNode.anchorPoint = ccp(0.5f,0.5f); + + // create the blend action + CCActionInterval * layerAction = [CCSequence actions: + [CCProgressFromTo actionWithDuration:duration_ from:100.f to:0.f], + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + // run the blend action + [outNode runAction: layerAction]; + + // add the layer (which contains our two rendertextures) to the scene + [self addChild: outNode z:2 tag:kSceneRadial]; +} + +// clean up on exit +-(void) onExit +{ + // remove our layer and release all containing objects + [self removeChildByTag:kSceneRadial cleanup:NO]; + [super onExit]; +} +@end + +#pragma mark - +#pragma mark Transition Radial CW + +@implementation CCTransitionRadialCW +-(CCProgressTimerType) radialType +{ + return kCCProgressTimerTypeRadialCW; +} +@end + diff --git a/libs/cocos2d/Platforms/CCGL.h b/libs/cocos2d/Platforms/CCGL.h new file mode 100644 index 0000000..9dc3e9b --- /dev/null +++ b/libs/cocos2d/Platforms/CCGL.h @@ -0,0 +1,82 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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. + */ + +// +// Common layer for OpenGL stuff +// + +#import + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import +#import +#import +#import "iOS/glu.h" +#import "iOS/EAGLView.h" + +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import +#import +#import // needed for NSOpenGLView +#import "Mac/MacGLView.h" +#endif + + +// iOS +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#define CC_GLVIEW EAGLView +#define ccglOrtho glOrthof +#define ccglClearDepth glClearDepthf +#define ccglGenerateMipmap glGenerateMipmapOES +#define ccglGenFramebuffers glGenFramebuffersOES +#define ccglBindFramebuffer glBindFramebufferOES +#define ccglFramebufferTexture2D glFramebufferTexture2DOES +#define ccglDeleteFramebuffers glDeleteFramebuffersOES +#define ccglCheckFramebufferStatus glCheckFramebufferStatusOES +#define ccglTranslate glTranslatef + +#define CC_GL_FRAMEBUFFER GL_FRAMEBUFFER_OES +#define CC_GL_FRAMEBUFFER_BINDING GL_FRAMEBUFFER_BINDING_OES +#define CC_GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_OES +#define CC_GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE_OES + +// Mac +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#define CC_GLVIEW MacGLView +#define ccglOrtho glOrtho +#define ccglClearDepth glClearDepth +#define ccglGenerateMipmap glGenerateMipmap +#define ccglGenFramebuffers glGenFramebuffers +#define ccglBindFramebuffer glBindFramebuffer +#define ccglFramebufferTexture2D glFramebufferTexture2D +#define ccglDeleteFramebuffers glDeleteFramebuffers +#define ccglCheckFramebufferStatus glCheckFramebufferStatus +#define ccglTranslate glTranslated + +#define CC_GL_FRAMEBUFFER GL_FRAMEBUFFER +#define CC_GL_FRAMEBUFFER_BINDING GL_FRAMEBUFFER_BINDING +#define CC_GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0 +#define CC_GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE + +#endif diff --git a/libs/cocos2d/Platforms/CCNS.h b/libs/cocos2d/Platforms/CCNS.h new file mode 100644 index 0000000..a8d5e0a --- /dev/null +++ b/libs/cocos2d/Platforms/CCNS.h @@ -0,0 +1,62 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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. + */ + +// +// Common layer for NS (Next-Step) stuff +// + +#import + +#import // for NSObject + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +#define CCRectFromString(__r__) CGRectFromString(__r__) +#define CCPointFromString(__p__) CGPointFromString(__p__) +#define CCSizeFromString(__s__) CGSizeFromString(__s__) +#define CCNSSizeToCGSize +#define CCNSRectToCGRect +#define CCNSPointToCGPoint +#define CCTextAlignment UITextAlignment +#define CCTextAlignmentCenter UITextAlignmentCenter +#define CCTextAlignmentLeft UITextAlignmentLeft +#define CCTextAlignmentRight UITextAlignmentRight + + +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +#define CCRectFromString(__r__) NSRectToCGRect( NSRectFromString(__r__) ) +#define CCPointFromString(__p__) NSPointToCGPoint( NSPointFromString(__p__) ) +#define CCSizeFromString(__s__) NSSizeToCGSize( NSSizeFromString(__s__) ) +#define CCNSSizeToCGSize NSSizeToCGSize +#define CCNSRectToCGRect NSRectToCGRect +#define CCNSPointToCGPoint NSPointToCGPoint +#define CCTextAlignment NSTextAlignment +#define CCTextAlignmentCenter NSCenterTextAlignment +#define CCTextAlignmentLeft NSLeftTextAlignment +#define CCTextAlignmentRight NSRightTextAlignment + +#endif + + diff --git a/libs/cocos2d/Platforms/Mac/CCDirectorMac.h b/libs/cocos2d/Platforms/Mac/CCDirectorMac.h new file mode 100644 index 0000000..d07f8f7 --- /dev/null +++ b/libs/cocos2d/Platforms/Mac/CCDirectorMac.h @@ -0,0 +1,102 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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. + */ + + +// Only compile this code on Mac. These files should not be included on your iOS project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +#import +#import "../../CCDirector.h" + +enum { + /// If the window is resized, it won't be autoscaled + kCCDirectorResize_NoScale, + /// If the window is resized, it will be autoscaled (default behavior) + kCCDirectorResize_AutoScale, +}; + +@interface CCDirector (MacExtension) +/** converts an NSEvent to GL coordinates */ +-(CGPoint) convertEventToGL:(NSEvent*)event; +@end + +/** Base class of Mac directors + @since v0.99.5 + */ +@interface CCDirectorMac : CCDirector +{ + BOOL isFullScreen_; + int resizeMode_; + CGPoint winOffset_; + CGSize originalWinSize_; + + NSWindow *fullScreenWindow_; + + // cache + NSWindow *windowGLView_; + NSView *superViewGLView_; + NSRect originalWinRect_; // Original size and position +} + +// whether or not the view is in fullscreen mode +@property (nonatomic, readonly) BOOL isFullScreen; + +// resize mode: with or without scaling +@property (nonatomic, readwrite) int resizeMode; + +@property (nonatomic, readwrite) CGSize originalWinSize; + +/** Sets the view in fullscreen or window mode */ +- (void) setFullScreen:(BOOL)fullscreen; + +/** Converts window size coordiantes to logical coordinates. + Useful only if resizeMode is kCCDirectorResize_Scale. + If resizeMode is kCCDirectorResize_NoScale, then no conversion will be done. +*/ +- (CGPoint) convertToLogicalCoordinates:(CGPoint)coordinates; +@end + + +/** DisplayLinkDirector is a Director that synchronizes timers with the refresh rate of the display. + * + * Features and Limitations: + * - Only available on 3.1+ + * - Scheduled timers & drawing are synchronizes with the refresh rate of the display + * - Only supports animation intervals of 1/60 1/30 & 1/15 + * + * It is the recommended Director if the SDK is 3.1 or newer + * + * @since v0.8.2 + */ +@interface CCDirectorDisplayLink : CCDirectorMac +{ + CVDisplayLinkRef displayLink; +} +@end + +#endif // __MAC_OS_X_VERSION_MAX_ALLOWED + diff --git a/libs/cocos2d/Platforms/Mac/CCDirectorMac.m b/libs/cocos2d/Platforms/Mac/CCDirectorMac.m new file mode 100644 index 0000000..b9bb327 --- /dev/null +++ b/libs/cocos2d/Platforms/Mac/CCDirectorMac.m @@ -0,0 +1,478 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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. + */ + +// Only compile this code on Mac. These files should not be included on your iOS project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +#import + +#import "CCDirectorMac.h" +#import "CCEventDispatcher.h" +#import "MacGLView.h" + +#import "../../CCNode.h" +#import "../../CCScheduler.h" +#import "../../ccMacros.h" + +#pragma mark - +#pragma mark Director Mac extensions + + +@interface CCDirector () +-(void) setNextScene; +-(void) showFPS; +-(void) calculateDeltaTime; +@end + +@implementation CCDirector (MacExtension) +-(CGPoint) convertEventToGL:(NSEvent*)event +{ + NSPoint point = [openGLView_ convertPoint:[event locationInWindow] fromView:nil]; + CGPoint p = NSPointToCGPoint(point); + + return [(CCDirectorMac*)self convertToLogicalCoordinates:p]; +} + +@end + +#pragma mark - +#pragma mark Director Mac + +@implementation CCDirectorMac + +@synthesize isFullScreen = isFullScreen_; +@synthesize originalWinSize = originalWinSize_; + +-(id) init +{ + if( (self = [super init]) ) { + isFullScreen_ = NO; + resizeMode_ = kCCDirectorResize_AutoScale; + + originalWinSize_ = CGSizeZero; + fullScreenWindow_ = nil; + windowGLView_ = nil; + winOffset_ = CGPointZero; + } + + return self; +} + +- (void) dealloc +{ + [superViewGLView_ release]; + [fullScreenWindow_ release]; + [windowGLView_ release]; + [super dealloc]; +} + +// +// setFullScreen code taken from GLFullScreen example by Apple +// +- (void) setFullScreen:(BOOL)fullscreen +{ + // Mac OS X 10.6 and later offer a simplified mechanism to create full-screen contexts +#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 + + if (isFullScreen_ == fullscreen) return; + + if( fullscreen ) { + originalWinRect_ = [openGLView_ frame]; + + // Cache normal window and superview of openGLView + if(!windowGLView_) + windowGLView_ = [[openGLView_ window] retain]; + + [superViewGLView_ release]; + superViewGLView_ = [[openGLView_ superview] retain]; + + + // Get screen size + NSRect displayRect = [[NSScreen mainScreen] frame]; + + // Create a screen-sized window on the display you want to take over + fullScreenWindow_ = [[MacWindow alloc] initWithFrame:displayRect fullscreen:YES]; + + // Remove glView from window + [openGLView_ removeFromSuperview]; + + // Set new frame + [openGLView_ setFrame:displayRect]; + + // Attach glView to fullscreen window + [fullScreenWindow_ setContentView:openGLView_]; + + // Show the fullscreen window + [fullScreenWindow_ makeMainWindow]; + [fullScreenWindow_ makeKeyAndOrderFront:self]; + + } else { + + // Remove glView from fullscreen window + [openGLView_ removeFromSuperview]; + + // Release fullscreen window + [fullScreenWindow_ release]; + fullScreenWindow_ = nil; + + // Attach glView to superview + [superViewGLView_ addSubview:openGLView_]; + + // Set new frame + [openGLView_ setFrame:originalWinRect_]; + + // Show the window + [windowGLView_ makeMainWindow]; + [windowGLView_ makeKeyAndOrderFront:self]; + } + isFullScreen_ = fullscreen; + + [openGLView_ retain]; // Retain +1 + + // re-configure glView + [self setOpenGLView:openGLView_]; + + [openGLView_ release]; // Retain -1 + + [openGLView_ setNeedsDisplay:YES]; +#else +#error Full screen is not supported for Mac OS 10.5 or older yet +#error If you don't want FullScreen support, you can safely remove these 2 lines +#endif +} + +-(void) setOpenGLView:(MacGLView *)view +{ + [super setOpenGLView:view]; + + // cache the NSWindow and NSOpenGLView created from the NIB + if( !isFullScreen_ && CGSizeEqualToSize(originalWinSize_, CGSizeZero)) + { + originalWinSize_ = winSizeInPixels_; + } +} + +-(int) resizeMode +{ + return resizeMode_; +} + +-(void) setResizeMode:(int)mode +{ + if( mode != resizeMode_ ) { + + resizeMode_ = mode; + + [self setProjection:projection_]; + [openGLView_ setNeedsDisplay: YES]; + } +} + +-(void) setProjection:(ccDirectorProjection)projection +{ + CGSize size = winSizeInPixels_; + + CGPoint offset = CGPointZero; + float widthAspect = size.width; + float heightAspect = size.height; + + + if( resizeMode_ == kCCDirectorResize_AutoScale && ! CGSizeEqualToSize(originalWinSize_, CGSizeZero ) ) { + + size = originalWinSize_; + + float aspect = originalWinSize_.width / originalWinSize_.height; + widthAspect = winSizeInPixels_.width; + heightAspect = winSizeInPixels_.width / aspect; + + if( heightAspect > winSizeInPixels_.height ) { + widthAspect = winSizeInPixels_.height * aspect; + heightAspect = winSizeInPixels_.height; + } + + winOffset_.x = (winSizeInPixels_.width - widthAspect) / 2; + winOffset_.y = (winSizeInPixels_.height - heightAspect) / 2; + + offset = winOffset_; + + } + + switch (projection) { + case kCCDirectorProjection2D: + glViewport(offset.x, offset.y, widthAspect, heightAspect); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + ccglOrtho(0, size.width, 0, size.height, -1024, 1024); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + break; + + case kCCDirectorProjection3D: + glViewport(offset.x, offset.y, widthAspect, heightAspect); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(60, (GLfloat)widthAspect/heightAspect, 0.1f, 1500.0f); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + float eyeZ = size.height * [self getZEye] / winSizeInPixels_.height; + + gluLookAt( size.width/2, size.height/2, eyeZ, + size.width/2, size.height/2, 0, + 0.0f, 1.0f, 0.0f); + break; + + case kCCDirectorProjectionCustom: + if( projectionDelegate_ ) + [projectionDelegate_ updateProjection]; + break; + + default: + CCLOG(@"cocos2d: Director: unrecognized projecgtion"); + break; + } + + projection_ = projection; +} + +// If scaling is supported, then it should always return the original size +// otherwise it should return the "real" size. +-(CGSize) winSize +{ + if( resizeMode_ == kCCDirectorResize_AutoScale ) + return originalWinSize_; + + return winSizeInPixels_; +} + +-(CGSize) winSizeInPixels +{ + return [self winSize]; +} + +- (CGPoint) convertToLogicalCoordinates:(CGPoint)coords +{ + CGPoint ret; + + if( resizeMode_ == kCCDirectorResize_NoScale ) + ret = coords; + + else { + + float x_diff = originalWinSize_.width / (winSizeInPixels_.width - winOffset_.x * 2); + float y_diff = originalWinSize_.height / (winSizeInPixels_.height - winOffset_.y * 2); + + float adjust_x = (winSizeInPixels_.width * x_diff - originalWinSize_.width ) / 2; + float adjust_y = (winSizeInPixels_.height * y_diff - originalWinSize_.height ) / 2; + + ret = CGPointMake( (x_diff * coords.x) - adjust_x, ( y_diff * coords.y ) - adjust_y ); + } + + return ret; +} +@end + + +#pragma mark - +#pragma mark DirectorDisplayLink + + +@implementation CCDirectorDisplayLink + +- (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime +{ +#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD + if( ! runningThread_ ) + runningThread_ = [NSThread currentThread]; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + [self drawScene]; + [[CCEventDispatcher sharedDispatcher] dispatchQueuedEvents]; + + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:nil]; + + [pool release]; + +#else + [self performSelector:@selector(drawScene) onThread:runningThread_ withObject:nil waitUntilDone:YES]; +#endif + + return kCVReturnSuccess; +} + +// This is the renderer output callback function +static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) +{ + CVReturn result = [(CCDirectorDisplayLink*)displayLinkContext getFrameForTime:outputTime]; + return result; +} + +- (void) startAnimation +{ +#if ! CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD + runningThread_ = [[NSThread alloc] initWithTarget:self selector:@selector(mainLoop) object:nil]; + [runningThread_ start]; +#endif + + gettimeofday( &lastUpdate_, NULL); + + // Create a display link capable of being used with all active displays + CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); + + // Set the renderer output callback function + CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, self); + + // Set the display link for the current renderer + CGLContextObj cglContext = [[openGLView_ openGLContext] CGLContextObj]; + CGLPixelFormatObj cglPixelFormat = [[openGLView_ pixelFormat] CGLPixelFormatObj]; + CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat); + + // Activate the display link + CVDisplayLinkStart(displayLink); +} + +- (void) stopAnimation +{ + if( displayLink ) { + CVDisplayLinkStop(displayLink); + CVDisplayLinkRelease(displayLink); + displayLink = NULL; + +#if ! CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD + [runningThread_ cancel]; + [runningThread_ release]; + runningThread_ = nil; +#endif + } +} + +-(void) dealloc +{ + if( displayLink ) { + CVDisplayLinkStop(displayLink); + CVDisplayLinkRelease(displayLink); + } + [super dealloc]; +} + +// +// Mac Director has its own thread +// +-(void) mainLoop +{ + while( ![[NSThread currentThread] isCancelled] ) { + // There is no autorelease pool when this method is called because it will be called from a background thread + // It's important to create one or you will leak objects + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + [[NSRunLoop currentRunLoop] run]; + + [pool release]; + } +} + +// +// Draw the Scene +// +- (void) drawScene +{ + // We draw on a secondary thread through the display link + // When resizing the view, -reshape is called automatically on the main thread + // Add a mutex around to avoid the threads accessing the context simultaneously when resizing + CGLLockContext([[openGLView_ openGLContext] CGLContextObj]); + [[openGLView_ openGLContext] makeCurrentContext]; + + /* calculate "global" dt */ + [self calculateDeltaTime]; + + /* tick before glClear: issue #533 */ + if( ! isPaused_ ) { + [[CCScheduler sharedScheduler] tick: dt]; + } + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + /* to avoid flickr, nextScene MUST be here: after tick and before draw. + XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */ + if( nextScene_ ) + [self setNextScene]; + + glPushMatrix(); + + + // By default enable VertexArray, ColorArray, TextureCoordArray and Texture2D + CC_ENABLE_DEFAULT_GL_STATES(); + + /* draw the scene */ + [runningScene_ visit]; + + /* draw the notification node */ + [notificationNode_ visit]; + + if( displayFPS_ ) + [self showFPS]; + +#if CC_ENABLE_PROFILERS + [self showProfilers]; +#endif + + CC_DISABLE_DEFAULT_GL_STATES(); + + glPopMatrix(); + + [[openGLView_ openGLContext] flushBuffer]; + CGLUnlockContext([[openGLView_ openGLContext] CGLContextObj]); +} + +// set the event dispatcher +-(void) setOpenGLView:(MacGLView *)view +{ + if( view != openGLView_ ) { + + [super setOpenGLView:view]; + + CCEventDispatcher *eventDispatcher = [CCEventDispatcher sharedDispatcher]; + [openGLView_ setEventDelegate: eventDispatcher]; + [eventDispatcher setDispatchEvents: YES]; + + // Enable Touches. Default no. + [view setAcceptsTouchEvents:NO]; +// [view setAcceptsTouchEvents:YES]; + + + // Synchronize buffer swaps with vertical refresh rate + [[view openGLContext] makeCurrentContext]; + GLint swapInt = 1; + [[view openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; + } +} + +@end + +#endif // __MAC_OS_X_VERSION_MAX_ALLOWED diff --git a/libs/cocos2d/Platforms/Mac/CCEventDispatcher.h b/libs/cocos2d/Platforms/Mac/CCEventDispatcher.h new file mode 100644 index 0000000..d1969e1 --- /dev/null +++ b/libs/cocos2d/Platforms/Mac/CCEventDispatcher.h @@ -0,0 +1,276 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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. + */ + +// Only compile this code on Mac. These files should not be included on your iOS project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +#import + +#import "MacGLView.h" +#import "../../Support/uthash.h" // hack: uthash needs to be imported before utlist to prevent warning +#import "../../Support/utlist.h" +#import "../../ccConfig.h" + +#pragma mark - +#pragma mark CCMouseEventDelegate + +/** CCMouseEventDelegate protocol. + Implement it in your node to receive any of mouse events + */ +@protocol CCMouseEventDelegate +@optional + +// +// left +// +/** called when the "mouseDown" event is received. + Return YES to avoid propagating the event to other delegates. + */ +-(BOOL) ccMouseDown:(NSEvent*)event; + +/** called when the "mouseDragged" event is received. + Return YES to avoid propagating the event to other delegates. + */ +-(BOOL) ccMouseDragged:(NSEvent*)event; + +/** called when the "mouseMoved" event is received. + Return YES to avoid propagating the event to other delegates. + By default, "mouseMoved" is disabled. To enable it, send the "setAcceptsMouseMovedEvents:YES" message to the main window. + */ +-(BOOL) ccMouseMoved:(NSEvent*)event; + +/** called when the "mouseUp" event is received. + Return YES to avoid propagating the event to other delegates. + */ +-(BOOL) ccMouseUp:(NSEvent*)event; + + +// +// right +// + +/** called when the "rightMouseDown" event is received. + Return YES to avoid propagating the event to other delegates. + */ +-(BOOL) ccRightMouseDown:(NSEvent*)event; + +/** called when the "rightMouseDragged" event is received. + Return YES to avoid propagating the event to other delegates. + */ +-(BOOL) ccRightMouseDragged:(NSEvent*)event; + +/** called when the "rightMouseUp" event is received. + Return YES to avoid propagating the event to other delegates. + */ +-(BOOL) ccRightMouseUp:(NSEvent*)event; + +// +// other +// + +/** called when the "otherMouseDown" event is received. + Return YES to avoid propagating the event to other delegates. + */ +-(BOOL) ccOtherMouseDown:(NSEvent*)event; + +/** called when the "otherMouseDragged" event is received. + Return YES to avoid propagating the event to other delegates. + */ +-(BOOL) ccOtherMouseDragged:(NSEvent*)event; + +/** called when the "otherMouseUp" event is received. + Return YES to avoid propagating the event to other delegates. + */ +-(BOOL) ccOtherMouseUp:(NSEvent*)event; + +// +// scroll wheel +// + +/** called when the "scrollWheel" event is received. + Return YES to avoid propagating the event to other delegates. + */ +- (BOOL)ccScrollWheel:(NSEvent *)theEvent; + + +// +// enter / exit +// + +/** called when the "mouseEntered" event is received. + Return YES to avoid propagating the event to other delegates. + */ +- (void)ccMouseEntered:(NSEvent *)theEvent; + +/** called when the "mouseExited" event is received. + Return YES to avoid propagating the event to other delegates. + */ +- (void)ccMouseExited:(NSEvent *)theEvent; + +@end + +#pragma mark - +#pragma mark CCKeyboardEventDelegate + +/** CCKeyboardEventDelegate protocol. + Implement it in your node to receive any of keyboard events + */ +@protocol CCKeyboardEventDelegate +@optional +/** called when the "keyUp" event is received. + Return YES to avoid propagating the event to other delegates. + */ +-(BOOL) ccKeyUp:(NSEvent*)event; + +/** called when the "keyDown" event is received. + Return YES to avoid propagating the event to other delegates. + */ +-(BOOL) ccKeyDown:(NSEvent*)event; +/** called when the "flagsChanged" event is received. + Return YES to avoid propagating the event to other delegates. + */ +-(BOOL) ccFlagsChanged:(NSEvent*)event; +@end + +#pragma mark - +#pragma mark CCTouchEventDelegate + +/** CCTouchEventDelegate protocol. + Implement it in your node to receive any of touch events + */ +@protocol CCTouchEventDelegate +@optional +/** called when the "touchesBegan" event is received. + Return YES to avoid propagating the event to other delegates. + */ +- (BOOL)ccTouchesBeganWithEvent:(NSEvent *)event; + +/** called when the "touchesMoved" event is received. + Return YES to avoid propagating the event to other delegates. + */ +- (BOOL)ccTouchesMovedWithEvent:(NSEvent *)event; + +/** called when the "touchesEnded" event is received. + Return YES to avoid propagating the event to other delegates. + */ +- (BOOL)ccTouchesEndedWithEvent:(NSEvent *)event; + +/** called when the "touchesCancelled" event is received. + Return YES to avoid propagating the event to other delegates. + */ +- (BOOL)ccTouchesCancelledWithEvent:(NSEvent *)event; + +@end + + +#pragma mark - +#pragma mark CCEventDispatcher + +struct _listEntry; + +/** CCEventDispatcher + + This is object is responsible for dispatching the events: + - Mouse events + - Keyboard events + - Touch events + + Only available on Mac + */ +@interface CCEventDispatcher : NSObject { + + BOOL dispatchEvents_; + + struct _listEntry *keyboardDelegates_; + struct _listEntry *mouseDelegates_; + struct _listEntry *touchDelegates_; +} + +@property (nonatomic, readwrite) BOOL dispatchEvents; + + +/** CCEventDispatcher singleton */ ++(CCEventDispatcher*) sharedDispatcher; + +#pragma mark CCEventDispatcher - Mouse + +/** Adds a mouse delegate to the dispatcher's list. + Delegates with a lower priority value will be called before higher priority values. + All the events will be propgated to all the delegates, unless the one delegate returns YES. + + IMPORTANT: The delegate will be retained. + */ +-(void) addMouseDelegate:(id) delegate priority:(NSInteger)priority; + +/** removes a mouse delegate */ +-(void) removeMouseDelegate:(id) delegate; + +/** Removes all mouse delegates, releasing all the delegates */ +-(void) removeAllMouseDelegates; + +#pragma mark CCEventDispatcher - Keyboard + +/** Adds a Keyboard delegate to the dispatcher's list. + Delegates with a lower priority value will be called before higher priority values. + All the events will be propgated to all the delegates, unless the one delegate returns YES. + + IMPORTANT: The delegate will be retained. + */ +-(void) addKeyboardDelegate:(id) delegate priority:(NSInteger)priority; + +/** removes a mouse delegate */ +-(void) removeKeyboardDelegate:(id) delegate; + +/** Removes all mouse delegates, releasing all the delegates */ +-(void) removeAllKeyboardDelegates; + +#pragma mark CCEventDispatcher - Touches + +/** Adds a Touch delegate to the dispatcher's list. + Delegates with a lower priority value will be called before higher priority values. + All the events will be propgated to all the delegates, unless the one delegate returns YES. + + IMPORTANT: The delegate will be retained. + */ +- (void)addTouchDelegate:(id)delegate priority:(NSInteger)priority; + +/** Removes a touch delegate */ +- (void)removeTouchDelegate:(id) delegate; + +/** Removes all touch delegates, releasing all the delegates */ +- (void)removeAllTouchDelegates; + +#pragma mark CCEventDispatcher - Dispatch Events + +#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD +-(void) dispatchQueuedEvents; +#endif + +@end + + +#endif // __MAC_OS_X_VERSION_MAX_ALLOWED diff --git a/libs/cocos2d/Platforms/Mac/CCEventDispatcher.m b/libs/cocos2d/Platforms/Mac/CCEventDispatcher.m new file mode 100644 index 0000000..0b33c90 --- /dev/null +++ b/libs/cocos2d/Platforms/Mac/CCEventDispatcher.m @@ -0,0 +1,644 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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. + */ + +// Only compile this code on Mac. These files should not be included on your iOS project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +#import "CCEventDispatcher.h" +#import "../../ccConfig.h" + +static CCEventDispatcher *sharedDispatcher = nil; + +enum { + // mouse + kCCImplementsMouseDown = 1 << 0, + kCCImplementsMouseMoved = 1 << 1, + kCCImplementsMouseDragged = 1 << 2, + kCCImplementsMouseUp = 1 << 3, + kCCImplementsRightMouseDown = 1 << 4, + kCCImplementsRightMouseDragged = 1 << 5, + kCCImplementsRightMouseUp = 1 << 6, + kCCImplementsOtherMouseDown = 1 << 7, + kCCImplementsOtherMouseDragged = 1 << 8, + kCCImplementsOtherMouseUp = 1 << 9, + kCCImplementsScrollWheel = 1 << 10, + kCCImplementsMouseEntered = 1 << 11, + kCCImplementsMouseExited = 1 << 12, + + kCCImplementsTouchesBegan = 1 << 13, + kCCImplementsTouchesMoved = 1 << 14, + kCCImplementsTouchesEnded = 1 << 15, + kCCImplementsTouchesCancelled = 1 << 16, + + // keyboard + kCCImplementsKeyUp = 1 << 0, + kCCImplementsKeyDown = 1 << 1, + kCCImplementsFlagsChanged = 1 << 2, +}; + + +typedef struct _listEntry +{ + struct _listEntry *prev, *next; + id delegate; + NSInteger priority; + NSUInteger flags; +} tListEntry; + + +#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD + +#define QUEUE_EVENT_MAX 128 +struct _eventQueue { + SEL selector; + NSEvent *event; +}; + +static struct _eventQueue eventQueue[QUEUE_EVENT_MAX]; +static int eventQueueCount; + +#endif // CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD + + +@implementation CCEventDispatcher + +@synthesize dispatchEvents=dispatchEvents_; + + ++(CCEventDispatcher*) sharedDispatcher +{ + @synchronized(self) { + if (sharedDispatcher == nil) + sharedDispatcher = [[self alloc] init]; // assignment not done here + } + return sharedDispatcher; +} + ++(id) allocWithZone:(NSZone *)zone +{ + @synchronized(self) { + NSAssert(sharedDispatcher == nil, @"Attempted to allocate a second instance of a singleton."); + return [super allocWithZone:zone]; + } + return nil; // on subsequent allocation attempts return nil +} + +-(id) init +{ + if( (self = [super init]) ) + { + // events enabled by default + dispatchEvents_ = YES; + + // delegates + keyboardDelegates_ = NULL; + mouseDelegates_ = NULL; + touchDelegates_ = NULL; + +#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD + eventQueueCount = 0; +#endif + } + + return self; +} + +- (void) dealloc +{ + [super dealloc]; +} + +#pragma mark CCEventDispatcher - add / remove delegates + +-(void) addDelegate:(id)delegate priority:(NSInteger)priority flags:(NSUInteger)flags list:(tListEntry**)list +{ + tListEntry *listElement = malloc( sizeof(*listElement) ); + + listElement->delegate = [delegate retain]; + listElement->priority = priority; + listElement->flags = flags; + listElement->next = listElement->prev = NULL; + + // empty list ? + if( ! *list ) { + DL_APPEND( *list, listElement ); + + } else { + BOOL added = NO; + + for( tListEntry *elem = *list; elem ; elem = elem->next ) { + if( priority < elem->priority ) { + + if( elem == *list ) + DL_PREPEND(*list, listElement); + else { + listElement->next = elem; + listElement->prev = elem->prev; + + elem->prev->next = listElement; + elem->prev = listElement; + } + + added = YES; + break; + } + } + + // Not added? priority has the higher value. Append it. + if( !added ) + DL_APPEND(*list, listElement); + } +} + +-(void) removeDelegate:(id)delegate fromList:(tListEntry**)list +{ + tListEntry *entry, *tmp; + + // updates with priority < 0 + DL_FOREACH_SAFE( *list, entry, tmp ) { + if( entry->delegate == delegate ) { + DL_DELETE( *list, entry ); + [delegate release]; + free(entry); + break; + } + } +} + +-(void) removeAllDelegatesFromList:(tListEntry**)list +{ + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( *list, entry, tmp ) { + DL_DELETE( *list, entry ); + free(entry); + } +} + + +-(void) addMouseDelegate:(id) delegate priority:(NSInteger)priority +{ + NSUInteger flags = 0; + + flags |= ( [delegate respondsToSelector:@selector(ccMouseDown:)] ? kCCImplementsMouseDown : 0 ); + flags |= ( [delegate respondsToSelector:@selector(ccMouseDragged:)] ? kCCImplementsMouseDragged : 0 ); + flags |= ( [delegate respondsToSelector:@selector(ccMouseMoved:)] ? kCCImplementsMouseMoved : 0 ); + flags |= ( [delegate respondsToSelector:@selector(ccMouseUp:)] ? kCCImplementsMouseUp : 0 ); + + flags |= ( [delegate respondsToSelector:@selector(ccRightMouseDown:)] ? kCCImplementsRightMouseDown : 0 ); + flags |= ( [delegate respondsToSelector:@selector(ccRightMouseDragged:)] ? kCCImplementsRightMouseDragged : 0 ); + flags |= ( [delegate respondsToSelector:@selector(ccRightMouseUp:)] ? kCCImplementsRightMouseUp : 0 ); + + flags |= ( [delegate respondsToSelector:@selector(ccOtherMouseDown:)] ? kCCImplementsOtherMouseDown : 0 ); + flags |= ( [delegate respondsToSelector:@selector(ccOtherMouseDragged:)] ? kCCImplementsOtherMouseDragged : 0 ); + flags |= ( [delegate respondsToSelector:@selector(ccOtherMouseUp:)] ? kCCImplementsOtherMouseUp : 0 ); + + flags |= ( [delegate respondsToSelector:@selector(ccMouseEntered:)] ? kCCImplementsMouseEntered : 0 ); + flags |= ( [delegate respondsToSelector:@selector(ccMouseExited:)] ? kCCImplementsMouseExited : 0 ); + + flags |= ( [delegate respondsToSelector:@selector(ccScrollWheel:)] ? kCCImplementsScrollWheel : 0 ); + + [self addDelegate:delegate priority:priority flags:flags list:&mouseDelegates_]; +} + +-(void) removeMouseDelegate:(id) delegate +{ + [self removeDelegate:delegate fromList:&mouseDelegates_]; +} + +-(void) removeAllMouseDelegates +{ + [self removeAllDelegatesFromList:&mouseDelegates_]; +} + +-(void) addKeyboardDelegate:(id) delegate priority:(NSInteger)priority +{ + NSUInteger flags = 0; + + flags |= ( [delegate respondsToSelector:@selector(ccKeyUp:)] ? kCCImplementsKeyUp : 0 ); + flags |= ( [delegate respondsToSelector:@selector(ccKeyDown:)] ? kCCImplementsKeyDown : 0 ); + flags |= ( [delegate respondsToSelector:@selector(ccFlagsChanged:)] ? kCCImplementsFlagsChanged : 0 ); + + [self addDelegate:delegate priority:priority flags:flags list:&keyboardDelegates_]; +} + +-(void) removeKeyboardDelegate:(id) delegate +{ + [self removeDelegate:delegate fromList:&keyboardDelegates_]; +} + +-(void) removeAllKeyboardDelegates +{ + [self removeAllDelegatesFromList:&keyboardDelegates_]; +} + +-(void) addTouchDelegate:(id) delegate priority:(NSInteger)priority +{ + NSUInteger flags = 0; + + flags |= ( [delegate respondsToSelector:@selector(ccTouchesBeganWithEvent:)] ? kCCImplementsTouchesBegan : 0 ); + flags |= ( [delegate respondsToSelector:@selector(ccTouchesMovedWithEvent:)] ? kCCImplementsTouchesMoved : 0 ); + flags |= ( [delegate respondsToSelector:@selector(ccTouchesEndedWithEvent:)] ? kCCImplementsTouchesEnded : 0 ); + flags |= ( [delegate respondsToSelector:@selector(ccTouchesCancelledWithEvent:)] ? kCCImplementsTouchesCancelled : 0 ); + + [self addDelegate:delegate priority:priority flags:flags list:&touchDelegates_]; +} + +-(void) removeTouchDelegate:(id) delegate +{ + [self removeDelegate:delegate fromList:&touchDelegates_]; +} + +-(void) removeAllTouchDelegates +{ + [self removeAllDelegatesFromList:&touchDelegates_]; +} + + +#pragma mark CCEventDispatcher - Mouse events +// +// Mouse events +// + +// +// Left +// +- (void)mouseDown:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsMouseDown ) { + void *swallows = [entry->delegate performSelector:@selector(ccMouseDown:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +- (void)mouseMoved:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsMouseMoved ) { + void *swallows = [entry->delegate performSelector:@selector(ccMouseMoved:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +- (void)mouseDragged:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsMouseDragged ) { + void *swallows = [entry->delegate performSelector:@selector(ccMouseDragged:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +- (void)mouseUp:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsMouseUp ) { + void *swallows = [entry->delegate performSelector:@selector(ccMouseUp:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +// +// Mouse Right +// +- (void)rightMouseDown:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsRightMouseDown ) { + void *swallows = [entry->delegate performSelector:@selector(ccRightMouseDown:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +- (void)rightMouseDragged:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsRightMouseDragged ) { + void *swallows = [entry->delegate performSelector:@selector(ccRightMouseDragged:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +- (void)rightMouseUp:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsRightMouseUp ) { + void *swallows = [entry->delegate performSelector:@selector(ccRightMouseUp:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +// +// Mouse Other +// +- (void)otherMouseDown:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsOtherMouseDown ) { + void *swallows = [entry->delegate performSelector:@selector(ccOtherMouseDown:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +- (void)otherMouseDragged:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsOtherMouseDragged ) { + void *swallows = [entry->delegate performSelector:@selector(ccOtherMouseDragged:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +- (void)otherMouseUp:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsOtherMouseUp ) { + void *swallows = [entry->delegate performSelector:@selector(ccOtherMouseUp:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +// +// Scroll Wheel +// +- (void)scrollWheel:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsScrollWheel ) { + void *swallows = [entry->delegate performSelector:@selector(ccScrollWheel:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +// +// Mouse enter / exit +- (void)mouseExited:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsMouseEntered ) { + void *swallows = [entry->delegate performSelector:@selector(ccMouseEntered:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +- (void)mouseEntered:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsMouseExited) { + void *swallows = [entry->delegate performSelector:@selector(ccMouseExited:) withObject:event]; + if( swallows ) + break; + } + } + } +} + + +#pragma mark CCEventDispatcher - Keyboard events + +// Keyboard events +- (void)keyDown:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( keyboardDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsKeyDown ) { + void *swallows = [entry->delegate performSelector:@selector(ccKeyDown:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +- (void)keyUp:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( keyboardDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsKeyUp ) { + void *swallows = [entry->delegate performSelector:@selector(ccKeyUp:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +- (void)flagsChanged:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( keyboardDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsFlagsChanged ) { + void *swallows = [entry->delegate performSelector:@selector(ccFlagsChanged:) withObject:event]; + if( swallows ) + break; + } + } + } +} + + +#pragma mark CCEventDispatcher - Touch events + +- (void)touchesBeganWithEvent:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( touchDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsTouchesBegan) { + void *swallows = [entry->delegate performSelector:@selector(ccTouchesBeganWithEvent:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +- (void)touchesMovedWithEvent:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( touchDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsTouchesMoved) { + void *swallows = [entry->delegate performSelector:@selector(ccTouchesMovedWithEvent:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +- (void)touchesEndedWithEvent:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( touchDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsTouchesEnded) { + void *swallows = [entry->delegate performSelector:@selector(ccTouchesEndedWithEvent:) withObject:event]; + if( swallows ) + break; + } + } + } +} + +- (void)touchesCancelledWithEvent:(NSEvent *)event +{ + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( touchDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsTouchesCancelled) { + void *swallows = [entry->delegate performSelector:@selector(ccTouchesCancelledWithEvent:) withObject:event]; + if( swallows ) + break; + } + } + } +} + + +#pragma mark CCEventDispatcher - queue events + +#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD +-(void) queueEvent:(NSEvent*)event selector:(SEL)selector +{ + NSAssert( eventQueueCount < QUEUE_EVENT_MAX, @"CCEventDispatcher: recompile. Increment QUEUE_EVENT_MAX value"); + + @synchronized (self) { + eventQueue[eventQueueCount].selector = selector; + eventQueue[eventQueueCount].event = [event copy]; + + eventQueueCount++; + } +} + +-(void) dispatchQueuedEvents +{ + @synchronized (self) { + for( int i=0; i < eventQueueCount; i++ ) { + SEL sel = eventQueue[i].selector; + NSEvent *event = eventQueue[i].event; + + [self performSelector:sel withObject:event]; + + [event release]; + } + + eventQueueCount = 0; + } +} +#endif // CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD + + +@end + +#endif // __MAC_OS_X_VERSION_MAX_ALLOWED \ No newline at end of file diff --git a/libs/cocos2d/Platforms/Mac/MacGLView.h b/libs/cocos2d/Platforms/Mac/MacGLView.h new file mode 100644 index 0000000..8d04b8e --- /dev/null +++ b/libs/cocos2d/Platforms/Mac/MacGLView.h @@ -0,0 +1,88 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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. + */ + +// Only compile this code on Mac. These files should not be included on your iOS project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +#import + +#import "../../ccConfig.h" + +//PROTOCOLS: + +@protocol MacEventDelegate +// Mouse +- (void)mouseDown:(NSEvent *)theEvent; +- (void)mouseUp:(NSEvent *)theEvent; +- (void)mouseMoved:(NSEvent *)theEvent; +- (void)mouseDragged:(NSEvent *)theEvent; +- (void)rightMouseDown:(NSEvent*)event; +- (void)rightMouseDragged:(NSEvent*)event; +- (void)rightMouseUp:(NSEvent*)event; +- (void)otherMouseDown:(NSEvent*)event; +- (void)otherMouseDragged:(NSEvent*)event; +- (void)otherMouseUp:(NSEvent*)event; +- (void)scrollWheel:(NSEvent *)theEvent; +- (void)mouseEntered:(NSEvent *)theEvent; +- (void)mouseExited:(NSEvent *)theEvent; + + +// Keyboard +- (void)keyDown:(NSEvent *)theEvent; +- (void)keyUp:(NSEvent *)theEvent; +- (void)flagsChanged:(NSEvent *)theEvent; + +// Touches +- (void)touchesBeganWithEvent:(NSEvent *)event; +- (void)touchesMovedWithEvent:(NSEvent *)event; +- (void)touchesEndedWithEvent:(NSEvent *)event; +- (void)touchesCancelledWithEvent:(NSEvent *)event; + +#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD +- (void)queueEvent:(NSEvent*)event selector:(SEL)selector; +#endif + +@end + +/** MacGLView + + Only available for Mac OS X + */ +@interface MacGLView : NSOpenGLView { + id eventDelegate_; +} + +@property (nonatomic, readwrite, assign) id eventDelegate; + +// initializes the MacGLView with a frame rect and an OpenGL context +- (id) initWithFrame:(NSRect)frameRect shareContext:(NSOpenGLContext*)context; + +// private ++(void) load_; +@end + +#endif // __MAC_OS_X_VERSION_MAX_ALLOWED \ No newline at end of file diff --git a/libs/cocos2d/Platforms/Mac/MacGLView.m b/libs/cocos2d/Platforms/Mac/MacGLView.m new file mode 100644 index 0000000..e5aa242 --- /dev/null +++ b/libs/cocos2d/Platforms/Mac/MacGLView.m @@ -0,0 +1,241 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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. + */ + +/* + * Idea of subclassing NSOpenGLView was taken from "TextureUpload" Apple's sample + */ + +// Only compile this code on Mac. These files should not be included on your iOS project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +#import "MacGLView.h" +#import + +#import "CCDirectorMac.h" +#import "../../ccConfig.h" + + +@implementation MacGLView + +@synthesize eventDelegate = eventDelegate_; + ++(void) load_ +{ + NSLog(@"%@ loaded", self); +} + +- (id) initWithFrame:(NSRect)frameRect +{ + self = [self initWithFrame:frameRect shareContext:nil]; + return self; +} + +- (id) initWithFrame:(NSRect)frameRect shareContext:(NSOpenGLContext*)context +{ + NSOpenGLPixelFormatAttribute attribs[] = + { + NSOpenGLPFAAccelerated, + NSOpenGLPFANoRecovery, + NSOpenGLPFADoubleBuffer, + NSOpenGLPFADepthSize, 24, + + 0 + }; + + NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; + + if (!pixelFormat) + NSLog(@"No OpenGL pixel format"); + + if( (self = [super initWithFrame:frameRect pixelFormat:[pixelFormat autorelease]]) ) { + + if( context ) + [self setOpenGLContext:context]; + + // Synchronize buffer swaps with vertical refresh rate + GLint swapInt = 1; + [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; + +// GLint order = -1; +// [[self openGLContext] setValues:&order forParameter:NSOpenGLCPSurfaceOrder]; + + // event delegate + eventDelegate_ = nil; + } + + return self; +} + +- (void) reshape +{ + // We draw on a secondary thread through the display link + // When resizing the view, -reshape is called automatically on the main thread + // Add a mutex around to avoid the threads accessing the context simultaneously when resizing + CGLLockContext([[self openGLContext] CGLContextObj]); + + NSRect rect = [self bounds]; + + CCDirector *director = [CCDirector sharedDirector]; + [director reshapeProjection: NSSizeToCGSize(rect.size) ]; + + // avoid flicker + [director drawScene]; +// [self setNeedsDisplay:YES]; + + CGLUnlockContext([[self openGLContext] CGLContextObj]); +} + +- (void) dealloc +{ + + [super dealloc]; +} + +#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD +#define DISPATCH_EVENT(__event__, __selector__) [eventDelegate_ queueEvent:__event__ selector:__selector__]; +#else +#define DISPATCH_EVENT(__event__, __selector__) \ + id obj = eventDelegate_; \ + [obj performSelector:__selector__ \ + onThread:[(CCDirectorMac*)[CCDirector sharedDirector] runningThread] \ + withObject:__event__ \ + waitUntilDone:NO]; +#endif + +#pragma mark MacGLView - Mouse events +- (void)mouseDown:(NSEvent *)theEvent +{ + DISPATCH_EVENT(theEvent, _cmd); +} + +- (void)mouseMoved:(NSEvent *)theEvent +{ + DISPATCH_EVENT(theEvent, _cmd); +} + +- (void)mouseDragged:(NSEvent *)theEvent +{ + DISPATCH_EVENT(theEvent, _cmd); +} + +- (void)mouseUp:(NSEvent *)theEvent +{ + DISPATCH_EVENT(theEvent, _cmd); +} + +- (void)rightMouseDown:(NSEvent *)theEvent { + DISPATCH_EVENT(theEvent, _cmd); +} + +- (void)rightMouseDragged:(NSEvent *)theEvent { + DISPATCH_EVENT(theEvent, _cmd); +} + +- (void)rightMouseUp:(NSEvent *)theEvent { + DISPATCH_EVENT(theEvent, _cmd); +} + +- (void)otherMouseDown:(NSEvent *)theEvent { + DISPATCH_EVENT(theEvent, _cmd); +} + +- (void)otherMouseDragged:(NSEvent *)theEvent { + DISPATCH_EVENT(theEvent, _cmd); +} + +- (void)otherMouseUp:(NSEvent *)theEvent { + DISPATCH_EVENT(theEvent, _cmd); +} + +- (void)mouseEntered:(NSEvent *)theEvent { + DISPATCH_EVENT(theEvent, _cmd); +} + +- (void)mouseExited:(NSEvent *)theEvent { + DISPATCH_EVENT(theEvent, _cmd); +} + +-(void) scrollWheel:(NSEvent *)theEvent { + DISPATCH_EVENT(theEvent, _cmd); +} + +#pragma mark MacGLView - Key events + +-(BOOL) becomeFirstResponder +{ + return YES; +} + +-(BOOL) acceptsFirstResponder +{ + return YES; +} + +-(BOOL) resignFirstResponder +{ + return YES; +} + +- (void)keyDown:(NSEvent *)theEvent +{ + DISPATCH_EVENT(theEvent, _cmd); +} + +- (void)keyUp:(NSEvent *)theEvent +{ + DISPATCH_EVENT(theEvent, _cmd); +} + +- (void)flagsChanged:(NSEvent *)theEvent +{ + DISPATCH_EVENT(theEvent, _cmd); +} + +#pragma mark MacGLView - Touch events +- (void)touchesBeganWithEvent:(NSEvent *)theEvent +{ + DISPATCH_EVENT(theEvent, _cmd); +} + +- (void)touchesMovedWithEvent:(NSEvent *)theEvent +{ + DISPATCH_EVENT(theEvent, _cmd); +} + +- (void)touchesEndedWithEvent:(NSEvent *)theEvent +{ + DISPATCH_EVENT(theEvent, _cmd); +} + +- (void)touchesCancelledWithEvent:(NSEvent *)theEvent +{ + DISPATCH_EVENT(theEvent, _cmd); +} + +@end + +#endif // __MAC_OS_X_VERSION_MAX_ALLOWED \ No newline at end of file diff --git a/libs/cocos2d/Platforms/Mac/MacWindow.h b/libs/cocos2d/Platforms/Mac/MacWindow.h new file mode 100644 index 0000000..716fe9b --- /dev/null +++ b/libs/cocos2d/Platforms/Mac/MacWindow.h @@ -0,0 +1,42 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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. + */ + +// Only compile this code on Mac. These files should not be included on your iOS project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +#import + + +@interface MacWindow : NSWindow +{ +} +- (id) initWithFrame:(NSRect)frame fullscreen:(BOOL)fullscreen; + +@end + + +#endif // __MAC_OS_X_VERSION_MAX_ALLOWED \ No newline at end of file diff --git a/libs/cocos2d/Platforms/Mac/MacWindow.m b/libs/cocos2d/Platforms/Mac/MacWindow.m new file mode 100644 index 0000000..28736a3 --- /dev/null +++ b/libs/cocos2d/Platforms/Mac/MacWindow.m @@ -0,0 +1,70 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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. + */ + +// Only compile this code on Mac. These files should not be included on your iOS project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +#import "MacWindow.h" + + +@implementation MacWindow + +- (id) initWithFrame:(NSRect)frame fullscreen:(BOOL)fullscreen +{ + int styleMask = fullscreen ? NSBackingStoreBuffered : ( NSTitledWindowMask | NSClosableWindowMask ); + self = [self initWithContentRect:frame + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:YES]; + + if (self != nil) + { + if(fullscreen) + { + [self setLevel:NSMainMenuWindowLevel+1]; + [self setHidesOnDeactivate:YES]; + [self setHasShadow:NO]; + } + + [self setAcceptsMouseMovedEvents:NO]; + [self setOpaque:YES]; + } + return self; +} + +- (BOOL) canBecomeKeyWindow +{ + return YES; +} + +- (BOOL) canBecomeMainWindow +{ + return YES; +} +@end + +#endif // __MAC_OS_X_VERSION_MAX_ALLOWED diff --git a/libs/cocos2d/Platforms/iOS/CCDirectorIOS.h b/libs/cocos2d/Platforms/iOS/CCDirectorIOS.h new file mode 100755 index 0000000..0d726a2 --- /dev/null +++ b/libs/cocos2d/Platforms/iOS/CCDirectorIOS.h @@ -0,0 +1,254 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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. + */ + + +// Only compile this code on iOS. These files should NOT be included on your Mac project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +#import "../../CCDirector.h" + +/** @typedef ccDeviceOrientation + Possible device orientations + */ +typedef enum { + /// Device oriented vertically, home button on the bottom + kCCDeviceOrientationPortrait = UIDeviceOrientationPortrait, + /// Device oriented vertically, home button on the top + kCCDeviceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown, + /// Device oriented horizontally, home button on the right + kCCDeviceOrientationLandscapeLeft = UIDeviceOrientationLandscapeLeft, + /// Device oriented horizontally, home button on the left + kCCDeviceOrientationLandscapeRight = UIDeviceOrientationLandscapeRight, + + // Backward compatibility stuff + CCDeviceOrientationPortrait = kCCDeviceOrientationPortrait, + CCDeviceOrientationPortraitUpsideDown = kCCDeviceOrientationPortraitUpsideDown, + CCDeviceOrientationLandscapeLeft = kCCDeviceOrientationLandscapeLeft, + CCDeviceOrientationLandscapeRight = kCCDeviceOrientationLandscapeRight, +} ccDeviceOrientation; + +/** @typedef ccDirectorType + Possible Director Types. + @since v0.8.2 + */ +typedef enum { + /** Will use a Director that triggers the main loop from an NSTimer object + * + * Features and Limitations: + * - Integrates OK with UIKit objects + * - It the slowest director + * - The invertal update is customizable from 1 to 60 + */ + kCCDirectorTypeNSTimer, + + /** will use a Director that triggers the main loop from a custom main loop. + * + * Features and Limitations: + * - Faster than NSTimer Director + * - It doesn't integrate well with UIKit objecgts + * - The interval update can't be customizable + */ + kCCDirectorTypeMainLoop, + + /** Will use a Director that triggers the main loop from a thread, but the main loop will be executed on the main thread. + * + * Features and Limitations: + * - Faster than NSTimer Director + * - It doesn't integrate well with UIKit objecgts + * - The interval update can't be customizable + */ + kCCDirectorTypeThreadMainLoop, + + /** Will use a Director that synchronizes timers with the refresh rate of the display. + * + * Features and Limitations: + * - Faster than NSTimer Director + * - Only available on 3.1+ + * - Scheduled timers & drawing are synchronizes with the refresh rate of the display + * - Integrates OK with UIKit objects + * - The interval update can be 1/60, 1/30, 1/15 + */ + kCCDirectorTypeDisplayLink, + + /** Default director is the NSTimer directory */ + kCCDirectorTypeDefault = kCCDirectorTypeNSTimer, + + // backward compatibility stuff + CCDirectorTypeNSTimer = kCCDirectorTypeNSTimer, + CCDirectorTypeMainLoop = kCCDirectorTypeMainLoop, + CCDirectorTypeThreadMainLoop = kCCDirectorTypeThreadMainLoop, + CCDirectorTypeDisplayLink = kCCDirectorTypeDisplayLink, + CCDirectorTypeDefault = kCCDirectorTypeDefault, + + +} ccDirectorType; + +/** CCDirector extensions for iPhone + */ +@interface CCDirector (iOSExtension) + +// rotates the screen if an orientation differnent than Portrait is used +-(void) applyOrientation; + +/** Sets the device orientation. + If the orientation is going to be controlled by an UIViewController, then the orientation should be Portrait + */ +-(void) setDeviceOrientation:(ccDeviceOrientation)orientation; + +/** returns the device orientation */ +-(ccDeviceOrientation) deviceOrientation; + +/** The size in pixels of the surface. It could be different than the screen size. + High-res devices might have a higher surface size than the screen size. + In non High-res device the contentScale will be emulated. + + The recommend way to enable Retina Display is by using the "enableRetinaDisplay:(BOOL)enabled" method. + + @since v0.99.4 + */ +-(void) setContentScaleFactor:(CGFloat)scaleFactor; + +/** Will enable Retina Display on devices that supports it. + It will enable Retina Display on iPhone4 and iPod Touch 4. + It will return YES, if it could enabled it, otherwise it will return NO. + + This is the recommened way to enable Retina Display. + @since v0.99.5 + */ +-(BOOL) enableRetinaDisplay:(BOOL)yes; + + +/** returns the content scale factor */ +-(CGFloat) contentScaleFactor; +@end + +@interface CCDirector (iOSExtensionClassMethods) + +/** There are 4 types of Director. + - kCCDirectorTypeNSTimer (default) + - kCCDirectorTypeMainLoop + - kCCDirectorTypeThreadMainLoop + - kCCDirectorTypeDisplayLink + + Each Director has it's own benefits, limitations. + If you are using SDK 3.1 or newer it is recommed to use the DisplayLink director + + This method should be called before any other call to the director. + + It will return NO if the director type is kCCDirectorTypeDisplayLink and the running SDK is < 3.1. Otherwise it will return YES. + + @since v0.8.2 + */ ++(BOOL) setDirectorType:(ccDirectorType) directorType; +@end + +#pragma mark - +#pragma mark CCDirectorIOS + +/** CCDirectorIOS: Base class of iOS directors + @since v0.99.5 + */ +@interface CCDirectorIOS : CCDirector +{ + /* orientation */ + ccDeviceOrientation deviceOrientation_; + + /* contentScaleFactor could be simulated */ + BOOL isContentScaleSupported_; + +} +@end + +/** FastDirector is a Director that triggers the main loop as fast as possible. + * + * Features and Limitations: + * - Faster than "normal" director + * - Consumes more battery than the "normal" director + * - It has some issues while using UIKit objects + */ +@interface CCDirectorFast : CCDirectorIOS +{ + BOOL isRunning; + + NSAutoreleasePool *autoreleasePool; +} +-(void) mainLoop; +@end + +/** ThreadedFastDirector is a Director that triggers the main loop from a thread. + * + * Features and Limitations: + * - Faster than "normal" director + * - Consumes more battery than the "normal" director + * - It can be used with UIKit objects + * + * @since v0.8.2 + */ +@interface CCDirectorFastThreaded : CCDirectorIOS +{ + BOOL isRunning; +} +-(void) mainLoop; +@end + +/** DisplayLinkDirector is a Director that synchronizes timers with the refresh rate of the display. + * + * Features and Limitations: + * - Only available on 3.1+ + * - Scheduled timers & drawing are synchronizes with the refresh rate of the display + * - Only supports animation intervals of 1/60 1/30 & 1/15 + * + * It is the recommended Director if the SDK is 3.1 or newer + * + * @since v0.8.2 + */ +@interface CCDirectorDisplayLink : CCDirectorIOS +{ + id displayLink; +} +-(void) mainLoop:(id)sender; +@end + +/** TimerDirector is a Director that calls the main loop from an NSTimer object + * + * Features and Limitations: + * - Integrates OK with UIKit objects + * - It the slowest director + * - The invertal update is customizable from 1 to 60 + * + * It is the default Director. + */ +@interface CCDirectorTimer : CCDirectorIOS +{ + NSTimer *animationTimer; +} +-(void) mainLoop; +@end + +// optimization. Should only be used to read it. Never to write it. +extern CGFloat __ccContentScaleFactor; + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED diff --git a/libs/cocos2d/Platforms/iOS/CCDirectorIOS.m b/libs/cocos2d/Platforms/iOS/CCDirectorIOS.m new file mode 100755 index 0000000..1feeda2 --- /dev/null +++ b/libs/cocos2d/Platforms/iOS/CCDirectorIOS.m @@ -0,0 +1,729 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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. + * + */ + +// Only compile this code on iOS. These files should NOT be included on your Mac project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +#import + +// cocos2d imports +#import "CCDirectorIOS.h" +#import "CCTouchDelegateProtocol.h" +#import "CCTouchDispatcher.h" +#import "../../CCScheduler.h" +#import "../../CCActionManager.h" +#import "../../CCTextureCache.h" +#import "../../ccMacros.h" +#import "../../CCScene.h" + +// support imports +#import "glu.h" +#import "../../Support/OpenGL_Internal.h" +#import "../../Support/CGPointExtension.h" + +#import "CCLayer.h" + +#if CC_ENABLE_PROFILERS +#import "../../Support/CCProfiling.h" +#endif + + +#pragma mark - +#pragma mark Director - global variables (optimization) + +CGFloat __ccContentScaleFactor = 1; + +#pragma mark - +#pragma mark Director iOS + +@interface CCDirector () +-(void) setNextScene; +-(void) showFPS; +-(void) calculateDeltaTime; +@end + +@implementation CCDirector (iOSExtensionClassMethods) + ++(Class) defaultDirector +{ + return [CCDirectorTimer class]; +} + ++ (BOOL) setDirectorType:(ccDirectorType)type +{ + if( type == CCDirectorTypeDisplayLink ) { + NSString *reqSysVer = @"3.1"; + NSString *currSysVer = [[UIDevice currentDevice] systemVersion]; + + if([currSysVer compare:reqSysVer options:NSNumericSearch] == NSOrderedAscending) + return NO; + } + switch (type) { + case CCDirectorTypeNSTimer: + [CCDirectorTimer sharedDirector]; + break; + case CCDirectorTypeDisplayLink: + [CCDirectorDisplayLink sharedDirector]; + break; + case CCDirectorTypeMainLoop: + [CCDirectorFast sharedDirector]; + break; + case CCDirectorTypeThreadMainLoop: + [CCDirectorFastThreaded sharedDirector]; + break; + default: + NSAssert(NO,@"Unknown director type"); + } + + return YES; +} + +@end + + + +#pragma mark - +#pragma mark CCDirectorIOS + +@interface CCDirectorIOS () +-(void) updateContentScaleFactor; + +@end + +@implementation CCDirectorIOS + +- (id) init +{ + if( (self=[super init]) ) { + + // portrait mode default + deviceOrientation_ = CCDeviceOrientationPortrait; + + __ccContentScaleFactor = 1; + isContentScaleSupported_ = NO; + + // running thread is main thread on iOS + runningThread_ = [NSThread currentThread]; + } + + return self; +} + +- (void) dealloc +{ + [super dealloc]; +} + +// +// Draw the Scene +// +- (void) drawScene +{ + /* calculate "global" dt */ + [self calculateDeltaTime]; + + /* tick before glClear: issue #533 */ + if( ! isPaused_ ) { + [[CCScheduler sharedScheduler] tick: dt]; + } + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + /* to avoid flickr, nextScene MUST be here: after tick and before draw. + XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */ + if( nextScene_ ) + [self setNextScene]; + + glPushMatrix(); + + [self applyOrientation]; + + // By default enable VertexArray, ColorArray, TextureCoordArray and Texture2D + CC_ENABLE_DEFAULT_GL_STATES(); + + /* draw the scene */ + [runningScene_ visit]; + + /* draw the notification node */ + [notificationNode_ visit]; + + if( displayFPS_ ) + [self showFPS]; + +#if CC_ENABLE_PROFILERS + [self showProfilers]; +#endif + + CC_DISABLE_DEFAULT_GL_STATES(); + + glPopMatrix(); + + [openGLView_ swapBuffers]; +} + +-(void) setProjection:(ccDirectorProjection)projection +{ + CGSize size = winSizeInPixels_; + + switch (projection) { + case kCCDirectorProjection2D: + glViewport(0, 0, size.width, size.height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + ccglOrtho(0, size.width, 0, size.height, -1024 * CC_CONTENT_SCALE_FACTOR(), 1024 * CC_CONTENT_SCALE_FACTOR()); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + break; + + case kCCDirectorProjection3D: + { + float zeye = [self getZEye]; + + glViewport(0, 0, size.width, size.height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); +// gluPerspective(60, (GLfloat)size.width/size.height, zeye-size.height/2, zeye+size.height/2 ); + gluPerspective(60, (GLfloat)size.width/size.height, 0.5f, 1500); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt( size.width/2, size.height/2, zeye, + size.width/2, size.height/2, 0, + 0.0f, 1.0f, 0.0f); + break; + } + + case kCCDirectorProjectionCustom: + if( projectionDelegate_ ) + [projectionDelegate_ updateProjection]; + break; + + default: + CCLOG(@"cocos2d: Director: unrecognized projecgtion"); + break; + } + + projection_ = projection; +} + +#pragma mark Director Integration with a UIKit view + +-(void) setOpenGLView:(EAGLView *)view +{ + if( view != openGLView_ ) { + + [super setOpenGLView:view]; + + // set size + winSizeInPixels_ = CGSizeMake(winSizeInPoints_.width * __ccContentScaleFactor, winSizeInPoints_.height *__ccContentScaleFactor); + + if( __ccContentScaleFactor != 1 ) + [self updateContentScaleFactor]; + + CCTouchDispatcher *touchDispatcher = [CCTouchDispatcher sharedDispatcher]; + [openGLView_ setTouchDelegate: touchDispatcher]; + [touchDispatcher setDispatchEvents: YES]; + } +} + +#pragma mark Director - Retina Display + +-(CGFloat) contentScaleFactor +{ + return __ccContentScaleFactor; +} + +-(void) setContentScaleFactor:(CGFloat)scaleFactor +{ + if( scaleFactor != __ccContentScaleFactor ) { + + __ccContentScaleFactor = scaleFactor; + winSizeInPixels_ = CGSizeMake( winSizeInPoints_.width * scaleFactor, winSizeInPoints_.height * scaleFactor ); + + if( openGLView_ ) + [self updateContentScaleFactor]; + + // update projection + [self setProjection:projection_]; + } +} + +-(void) updateContentScaleFactor +{ + // Based on code snippet from: http://developer.apple.com/iphone/prerelease/library/snippets/sp2010/sp28.html + if ([openGLView_ respondsToSelector:@selector(setContentScaleFactor:)]) + { + [openGLView_ setContentScaleFactor: __ccContentScaleFactor]; + + isContentScaleSupported_ = YES; + } + else + CCLOG(@"cocos2d: 'setContentScaleFactor:' is not supported on this device"); +} + +-(BOOL) enableRetinaDisplay:(BOOL)enabled +{ + // Already enabled ? + if( enabled && __ccContentScaleFactor == 2 ) + return YES; + + // Already disabled + if( ! enabled && __ccContentScaleFactor == 1 ) + return YES; + + // setContentScaleFactor is not supported + if (! [openGLView_ respondsToSelector:@selector(setContentScaleFactor:)]) + return NO; + + // SD device + if ([[UIScreen mainScreen] scale] == 1.0) + return NO; + + float newScale = enabled ? 2 : 1; + [self setContentScaleFactor:newScale]; + + return YES; +} + +// overriden, don't call super +-(void) reshapeProjection:(CGSize)size +{ + winSizeInPoints_ = [openGLView_ bounds].size; + winSizeInPixels_ = CGSizeMake(winSizeInPoints_.width * __ccContentScaleFactor, winSizeInPoints_.height *__ccContentScaleFactor); + + [self setProjection:projection_]; +} + +#pragma mark Director Scene Landscape + +-(CGPoint)convertToGL:(CGPoint)uiPoint +{ + CGSize s = winSizeInPoints_; + float newY = s.height - uiPoint.y; + float newX = s.width - uiPoint.x; + + CGPoint ret = CGPointZero; + switch ( deviceOrientation_) { + case CCDeviceOrientationPortrait: + ret = ccp( uiPoint.x, newY ); + break; + case CCDeviceOrientationPortraitUpsideDown: + ret = ccp(newX, uiPoint.y); + break; + case CCDeviceOrientationLandscapeLeft: + ret.x = uiPoint.y; + ret.y = uiPoint.x; + break; + case CCDeviceOrientationLandscapeRight: + ret.x = newY; + ret.y = newX; + break; + } + return ret; +} + +-(CGPoint)convertToUI:(CGPoint)glPoint +{ + CGSize winSize = winSizeInPoints_; + int oppositeX = winSize.width - glPoint.x; + int oppositeY = winSize.height - glPoint.y; + CGPoint uiPoint = CGPointZero; + switch ( deviceOrientation_) { + case CCDeviceOrientationPortrait: + uiPoint = ccp(glPoint.x, oppositeY); + break; + case CCDeviceOrientationPortraitUpsideDown: + uiPoint = ccp(oppositeX, glPoint.y); + break; + case CCDeviceOrientationLandscapeLeft: + uiPoint = ccp(glPoint.y, glPoint.x); + break; + case CCDeviceOrientationLandscapeRight: + // Can't use oppositeX/Y because x/y are flipped + uiPoint = ccp(winSize.width-glPoint.y, winSize.height-glPoint.x); + break; + } + return uiPoint; +} + +// get the current size of the glview +-(CGSize) winSize +{ + CGSize s = winSizeInPoints_; + + if( deviceOrientation_ == CCDeviceOrientationLandscapeLeft || deviceOrientation_ == CCDeviceOrientationLandscapeRight ) { + // swap x,y in landscape mode + CGSize tmp = s; + s.width = tmp.height; + s.height = tmp.width; + } + return s; +} + +-(CGSize) winSizeInPixels +{ + CGSize s = [self winSize]; + + s.width *= CC_CONTENT_SCALE_FACTOR(); + s.height *= CC_CONTENT_SCALE_FACTOR(); + + return s; +} + +-(ccDeviceOrientation) deviceOrientation +{ + return deviceOrientation_; +} + +- (void) setDeviceOrientation:(ccDeviceOrientation) orientation +{ + if( deviceOrientation_ != orientation ) { + deviceOrientation_ = orientation; + switch( deviceOrientation_) { + case CCDeviceOrientationPortrait: + [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortrait animated:NO]; + break; + case CCDeviceOrientationPortraitUpsideDown: + [[UIApplication sharedApplication] setStatusBarOrientation: UIDeviceOrientationPortraitUpsideDown animated:NO]; + break; + case CCDeviceOrientationLandscapeLeft: + [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeRight animated:NO]; + break; + case CCDeviceOrientationLandscapeRight: + [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeLeft animated:NO]; + break; + default: + NSLog(@"Director: Unknown device orientation"); + break; + } + } +} + +-(void) applyOrientation +{ + CGSize s = winSizeInPixels_; + float w = s.width / 2; + float h = s.height / 2; + + // XXX it's using hardcoded values. + // What if the the screen size changes in the future? + switch ( deviceOrientation_ ) { + case CCDeviceOrientationPortrait: + // nothing + break; + case CCDeviceOrientationPortraitUpsideDown: + // upside down + glTranslatef(w,h,0); + glRotatef(180,0,0,1); + glTranslatef(-w,-h,0); + break; + case CCDeviceOrientationLandscapeRight: + glTranslatef(w,h,0); + glRotatef(90,0,0,1); + glTranslatef(-h,-w,0); + break; + case CCDeviceOrientationLandscapeLeft: + glTranslatef(w,h,0); + glRotatef(-90,0,0,1); + glTranslatef(-h,-w,0); + break; + } +} + +-(void) end +{ + // don't release the event handlers + // They are needed in case the director is run again + [[CCTouchDispatcher sharedDispatcher] removeAllDelegates]; + + [super end]; +} + +@end + + +#pragma mark - +#pragma mark Director TimerDirector + +@implementation CCDirectorTimer +- (void)startAnimation +{ + NSAssert( animationTimer == nil, @"animationTimer must be nil. Calling startAnimation twice?"); + + if( gettimeofday( &lastUpdate_, NULL) != 0 ) { + CCLOG(@"cocos2d: Director: Error in gettimeofday"); + } + + animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval_ target:self selector:@selector(mainLoop) userInfo:nil repeats:YES]; + + // + // If you want to attach the opengl view into UIScrollView + // uncomment this line to prevent 'freezing'. + // It doesn't work on with the Fast Director + // + // [[NSRunLoop currentRunLoop] addTimer:animationTimer + // forMode:NSRunLoopCommonModes]; +} + +-(void) mainLoop +{ + [self drawScene]; +} + +- (void)stopAnimation +{ + [animationTimer invalidate]; + animationTimer = nil; +} + +- (void)setAnimationInterval:(NSTimeInterval)interval +{ + animationInterval_ = interval; + + if(animationTimer) { + [self stopAnimation]; + [self startAnimation]; + } +} + +-(void) dealloc +{ + [animationTimer release]; + [super dealloc]; +} +@end + + +#pragma mark - +#pragma mark Director DirectorFast + +@implementation CCDirectorFast + +- (id) init +{ + if(( self = [super init] )) { + +#if CC_DIRECTOR_DISPATCH_FAST_EVENTS + CCLOG(@"cocos2d: Fast Events enabled"); +#else + CCLOG(@"cocos2d: Fast Events disabled"); +#endif + isRunning = NO; + + // XXX: + // XXX: Don't create any autorelease object before calling "fast director" + // XXX: else it will be leaked + // XXX: + autoreleasePool = [NSAutoreleasePool new]; + } + + return self; +} + +- (void) startAnimation +{ + // XXX: + // XXX: release autorelease objects created + // XXX: between "use fast director" and "runWithScene" + // XXX: + [autoreleasePool release]; + autoreleasePool = nil; + + if ( gettimeofday( &lastUpdate_, NULL) != 0 ) { + CCLOG(@"cocos2d: Director: Error in gettimeofday"); + } + + + isRunning = YES; + + SEL selector = @selector(mainLoop); + NSMethodSignature* sig = [[[CCDirector sharedDirector] class] + instanceMethodSignatureForSelector:selector]; + NSInvocation* invocation = [NSInvocation + invocationWithMethodSignature:sig]; + [invocation setTarget:[CCDirector sharedDirector]]; + [invocation setSelector:selector]; + [invocation performSelectorOnMainThread:@selector(invokeWithTarget:) + withObject:[CCDirector sharedDirector] waitUntilDone:NO]; + +// NSInvocationOperation *loopOperation = [[[NSInvocationOperation alloc] +// initWithTarget:self selector:@selector(mainLoop) object:nil] +// autorelease]; +// +// [loopOperation performSelectorOnMainThread:@selector(start) withObject:nil +// waitUntilDone:NO]; +} + +-(void) mainLoop +{ + while (isRunning) { + + NSAutoreleasePool *loopPool = [NSAutoreleasePool new]; + +#if CC_DIRECTOR_DISPATCH_FAST_EVENTS + while( CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.004f, FALSE) == kCFRunLoopRunHandledSource); +#else + while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource); +#endif + + if (isPaused_) { + usleep(250000); // Sleep for a quarter of a second (250,000 microseconds) so that the framerate is 4 fps. + } + + [self drawScene]; + +#if CC_DIRECTOR_DISPATCH_FAST_EVENTS + while( CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.004f, FALSE) == kCFRunLoopRunHandledSource); +#else + while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource); +#endif + + [loopPool release]; + } +} +- (void) stopAnimation +{ + isRunning = NO; +} + +- (void)setAnimationInterval:(NSTimeInterval)interval +{ + NSLog(@"FastDirectory doesn't support setAnimationInterval, yet"); +} +@end + +#pragma mark - +#pragma mark Director DirectorThreadedFast + +@implementation CCDirectorFastThreaded + +- (id) init +{ + if(( self = [super init] )) { + isRunning = NO; + } + + return self; +} + +- (void) startAnimation +{ + + if ( gettimeofday( &lastUpdate_, NULL) != 0 ) { + CCLOG(@"cocos2d: ThreadedFastDirector: Error on gettimeofday"); + } + + isRunning = YES; + + NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(mainLoop) object:nil]; + [thread start]; + [thread release]; +} + +-(void) mainLoop +{ + while( ![[NSThread currentThread] isCancelled] ) { + if( isRunning ) + [self performSelectorOnMainThread:@selector(drawScene) withObject:nil waitUntilDone:YES]; + + if (isPaused_) { + usleep(250000); // Sleep for a quarter of a second (250,000 microseconds) so that the framerate is 4 fps. + } else { +// usleep(2000); + } + } +} +- (void) stopAnimation +{ + isRunning = NO; +} + +- (void)setAnimationInterval:(NSTimeInterval)interval +{ + NSLog(@"FastDirector doesn't support setAnimationInterval, yet"); +} +@end + +#pragma mark - +#pragma mark DirectorDisplayLink + +// Allows building DisplayLinkDirector for pre-3.1 SDKS +// without getting compiler warnings. +@interface NSObject(CADisplayLink) ++ (id) displayLinkWithTarget:(id)arg1 selector:(SEL)arg2; +- (void) addToRunLoop:(id)arg1 forMode:(id)arg2; +- (void) setFrameInterval:(int)interval; +- (void) invalidate; +@end + +@implementation CCDirectorDisplayLink + +- (void)setAnimationInterval:(NSTimeInterval)interval +{ + animationInterval_ = interval; + if(displayLink){ + [self stopAnimation]; + [self startAnimation]; + } +} + +- (void) startAnimation +{ + if ( gettimeofday( &lastUpdate_, NULL) != 0 ) { + CCLOG(@"cocos2d: DisplayLinkDirector: Error on gettimeofday"); + } + + // approximate frame rate + // assumes device refreshes at 60 fps + int frameInterval = (int) floor(animationInterval_ * 60.0f); + + CCLOG(@"cocos2d: Frame interval: %d", frameInterval); + + displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(mainLoop:)]; + [displayLink setFrameInterval:frameInterval]; + [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; +} + +-(void) mainLoop:(id)sender +{ + [self drawScene]; +} + +- (void) stopAnimation +{ + [displayLink invalidate]; + displayLink = nil; +} + +-(void) dealloc +{ + [displayLink release]; + [super dealloc]; +} +@end + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED diff --git a/libs/cocos2d/Platforms/iOS/CCTouchDelegateProtocol.h b/libs/cocos2d/Platforms/iOS/CCTouchDelegateProtocol.h new file mode 100644 index 0000000..20ba036 --- /dev/null +++ b/libs/cocos2d/Platforms/iOS/CCTouchDelegateProtocol.h @@ -0,0 +1,75 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Valentin Milea + * + * 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. + * + */ + +// Only compile this code on iOS. These files should NOT be included on your Mac project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +#import + +/** + CCTargetedTouchDelegate. + + Using this type of delegate results in two benefits: + 1. You don't need to deal with NSSets, the dispatcher does the job of splitting + them. You get exactly one UITouch per call. + 2. You can *claim* a UITouch by returning YES in ccTouchBegan. Updates of claimed + touches are sent only to the delegate(s) that claimed them. So if you get a move/ + ended/cancelled update you're sure it's your touch. This frees you from doing a + lot of checks when doing multi-touch. + + (The name TargetedTouchDelegate relates to updates "targeting" their specific + handler, without bothering the other handlers.) + @since v0.8 + */ +@protocol CCTargetedTouchDelegate + +/** Return YES to claim the touch. + @since v0.8 + */ +- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event; +@optional +// touch updates: +- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event; +- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event; +- (void)ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event; +@end + +/** + CCStandardTouchDelegate. + + This type of delegate is the same one used by CocoaTouch. You will receive all the events (Began,Moved,Ended,Cancelled). + @since v0.8 +*/ +@protocol CCStandardTouchDelegate +@optional +- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; +- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; +- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; +- (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; +@end + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED diff --git a/libs/cocos2d/Platforms/iOS/CCTouchDispatcher.h b/libs/cocos2d/Platforms/iOS/CCTouchDispatcher.h new file mode 100644 index 0000000..b692c6d --- /dev/null +++ b/libs/cocos2d/Platforms/iOS/CCTouchDispatcher.h @@ -0,0 +1,122 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Valentin Milea + * + * 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. + * + */ + +// Only compile this code on iOS. These files should NOT be included on your Mac project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +#import "CCTouchDelegateProtocol.h" +#import "EAGLView.h" + + +typedef enum +{ + kCCTouchSelectorBeganBit = 1 << 0, + kCCTouchSelectorMovedBit = 1 << 1, + kCCTouchSelectorEndedBit = 1 << 2, + kCCTouchSelectorCancelledBit = 1 << 3, + kCCTouchSelectorAllBits = ( kCCTouchSelectorBeganBit | kCCTouchSelectorMovedBit | kCCTouchSelectorEndedBit | kCCTouchSelectorCancelledBit), +} ccTouchSelectorFlag; + + +enum { + kCCTouchBegan, + kCCTouchMoved, + kCCTouchEnded, + kCCTouchCancelled, + + kCCTouchMax, +}; + +struct ccTouchHandlerHelperData { + SEL touchesSel; + SEL touchSel; + ccTouchSelectorFlag type; +}; + +/** CCTouchDispatcher. + Singleton that handles all the touch events. + The dispatcher dispatches events to the registered TouchHandlers. + There are 2 different type of touch handlers: + - Standard Touch Handlers + - Targeted Touch Handlers + + The Standard Touch Handlers work like the CocoaTouch touch handler: a set of touches is passed to the delegate. + On the other hand, the Targeted Touch Handlers only receive 1 touch at the time, and they can "swallow" touches (avoid the propagation of the event). + + Firstly, the dispatcher sends the received touches to the targeted touches. + These touches can be swallowed by the Targeted Touch Handlers. If there are still remaining touches, then the remaining touches will be sent + to the Standard Touch Handlers. + + @since v0.8.0 + */ +@interface CCTouchDispatcher : NSObject +{ + NSMutableArray *targetedHandlers; + NSMutableArray *standardHandlers; + + BOOL locked; + BOOL toAdd; + BOOL toRemove; + NSMutableArray *handlersToAdd; + NSMutableArray *handlersToRemove; + BOOL toQuit; + + BOOL dispatchEvents; + + // 4, 1 for each type of event + struct ccTouchHandlerHelperData handlerHelperData[kCCTouchMax]; +} + +/** singleton of the CCTouchDispatcher */ ++ (CCTouchDispatcher*)sharedDispatcher; + +/** Whether or not the events are going to be dispatched. Default: YES */ +@property (nonatomic,readwrite, assign) BOOL dispatchEvents; + +/** Adds a standard touch delegate to the dispatcher's list. + See StandardTouchDelegate description. + IMPORTANT: The delegate will be retained. + */ +-(void) addStandardDelegate:(id) delegate priority:(int)priority; +/** Adds a targeted touch delegate to the dispatcher's list. + See TargetedTouchDelegate description. + IMPORTANT: The delegate will be retained. + */ +-(void) addTargetedDelegate:(id) delegate priority:(int)priority swallowsTouches:(BOOL)swallowsTouches; +/** Removes a touch delegate. + The delegate will be released + */ +-(void) removeDelegate:(id) delegate; +/** Removes all touch delegates, releasing all the delegates */ +-(void) removeAllDelegates; +/** Changes the priority of a previously added delegate. The lower the number, + the higher the priority */ +-(void) setPriority:(int) priority forDelegate:(id) delegate; + +@end + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED diff --git a/libs/cocos2d/Platforms/iOS/CCTouchDispatcher.m b/libs/cocos2d/Platforms/iOS/CCTouchDispatcher.m new file mode 100644 index 0000000..7868a87 --- /dev/null +++ b/libs/cocos2d/Platforms/iOS/CCTouchDispatcher.m @@ -0,0 +1,326 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Valentin Milea + * + * 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. + * + */ + +// Only compile this code on iOS. These files should NOT be included on your Mac project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + + +#import "CCTouchDispatcher.h" +#import "CCTouchHandler.h" + + +@implementation CCTouchDispatcher + +@synthesize dispatchEvents; + +static CCTouchDispatcher *sharedDispatcher = nil; + ++(CCTouchDispatcher*) sharedDispatcher +{ + @synchronized(self) { + if (sharedDispatcher == nil) + sharedDispatcher = [[self alloc] init]; // assignment not done here + } + return sharedDispatcher; +} + ++(id) allocWithZone:(NSZone *)zone +{ + @synchronized(self) { + NSAssert(sharedDispatcher == nil, @"Attempted to allocate a second instance of a singleton."); + return [super allocWithZone:zone]; + } + return nil; // on subsequent allocation attempts return nil +} + +-(id) init +{ + if((self = [super init])) { + + dispatchEvents = YES; + targetedHandlers = [[NSMutableArray alloc] initWithCapacity:8]; + standardHandlers = [[NSMutableArray alloc] initWithCapacity:4]; + + handlersToAdd = [[NSMutableArray alloc] initWithCapacity:8]; + handlersToRemove = [[NSMutableArray alloc] initWithCapacity:8]; + + toRemove = NO; + toAdd = NO; + toQuit = NO; + locked = NO; + + handlerHelperData[kCCTouchBegan] = (struct ccTouchHandlerHelperData) {@selector(ccTouchesBegan:withEvent:),@selector(ccTouchBegan:withEvent:),kCCTouchSelectorBeganBit}; + handlerHelperData[kCCTouchMoved] = (struct ccTouchHandlerHelperData) {@selector(ccTouchesMoved:withEvent:),@selector(ccTouchMoved:withEvent:),kCCTouchSelectorMovedBit}; + handlerHelperData[kCCTouchEnded] = (struct ccTouchHandlerHelperData) {@selector(ccTouchesEnded:withEvent:),@selector(ccTouchEnded:withEvent:),kCCTouchSelectorEndedBit}; + handlerHelperData[kCCTouchCancelled] = (struct ccTouchHandlerHelperData) {@selector(ccTouchesCancelled:withEvent:),@selector(ccTouchCancelled:withEvent:),kCCTouchSelectorCancelledBit}; + + } + + return self; +} + +-(void) dealloc +{ + [targetedHandlers release]; + [standardHandlers release]; + [handlersToAdd release]; + [handlersToRemove release]; + [super dealloc]; +} + +// +// handlers management +// + +#pragma mark TouchDispatcher - Add Hanlder + +-(void) forceAddHandler:(CCTouchHandler*)handler array:(NSMutableArray*)array +{ + NSUInteger i = 0; + + for( CCTouchHandler *h in array ) { + if( h.priority < handler.priority ) + i++; + + NSAssert( h.delegate != handler.delegate, @"Delegate already added to touch dispatcher."); + } + [array insertObject:handler atIndex:i]; +} + +-(void) addStandardDelegate:(id) delegate priority:(int)priority +{ + CCTouchHandler *handler = [CCStandardTouchHandler handlerWithDelegate:delegate priority:priority]; + if( ! locked ) { + [self forceAddHandler:handler array:standardHandlers]; + } else { + [handlersToAdd addObject:handler]; + toAdd = YES; + } +} + +-(void) addTargetedDelegate:(id) delegate priority:(int)priority swallowsTouches:(BOOL)swallowsTouches +{ + CCTouchHandler *handler = [CCTargetedTouchHandler handlerWithDelegate:delegate priority:priority swallowsTouches:swallowsTouches]; + if( ! locked ) { + [self forceAddHandler:handler array:targetedHandlers]; + } else { + [handlersToAdd addObject:handler]; + toAdd = YES; + } +} + +#pragma mark TouchDispatcher - removeDelegate + +-(void) forceRemoveDelegate:(id)delegate +{ + // XXX: remove it from both handlers ??? + + for( CCTouchHandler *handler in targetedHandlers ) { + if( handler.delegate == delegate ) { + [targetedHandlers removeObject:handler]; + break; + } + } + + for( CCTouchHandler *handler in standardHandlers ) { + if( handler.delegate == delegate ) { + [standardHandlers removeObject:handler]; + break; + } + } +} + +-(void) removeDelegate:(id) delegate +{ + if( delegate == nil ) + return; + + if( ! locked ) { + [self forceRemoveDelegate:delegate]; + } else { + [handlersToRemove addObject:delegate]; + toRemove = YES; + } +} + +#pragma mark TouchDispatcher - removeAllDelegates + +-(void) forceRemoveAllDelegates +{ + [standardHandlers removeAllObjects]; + [targetedHandlers removeAllObjects]; +} +-(void) removeAllDelegates +{ + if( ! locked ) + [self forceRemoveAllDelegates]; + else + toQuit = YES; +} + +#pragma mark Changing priority of added handlers + +-(void) setPriority:(int) priority forDelegate:(id) delegate +{ + NSAssert(NO, @"Set priority no implemented yet. Don't forget to report this bug!"); +// if( delegate == nil ) +// [NSException raise:NSInvalidArgumentException format:@"Got nil touch delegate"]; +// +// CCTouchHandler *handler = nil; +// for( handler in touchHandlers ) +// if( handler.delegate == delegate ) break; +// +// if( handler == nil ) +// [NSException raise:NSInvalidArgumentException format:@"Touch delegate not found"]; +// +// if( handler.priority != priority ) { +// handler.priority = priority; +// +// [handler retain]; +// [touchHandlers removeObject:handler]; +// [self addHandler:handler]; +// [handler release]; +// } +} + + +// +// dispatch events +// +-(void) touches:(NSSet*)touches withEvent:(UIEvent*)event withTouchType:(unsigned int)idx +{ + NSAssert(idx < 4, @"Invalid idx value"); + + id mutableTouches; + locked = YES; + + // optimization to prevent a mutable copy when it is not necessary + unsigned int targetedHandlersCount = [targetedHandlers count]; + unsigned int standardHandlersCount = [standardHandlers count]; + BOOL needsMutableSet = (targetedHandlersCount && standardHandlersCount); + + mutableTouches = (needsMutableSet ? [touches mutableCopy] : touches); + + struct ccTouchHandlerHelperData helper = handlerHelperData[idx]; + // + // process the target handlers 1st + // + if( targetedHandlersCount > 0 ) { + for( UITouch *touch in touches ) { + for(CCTargetedTouchHandler *handler in targetedHandlers) { + + BOOL claimed = NO; + if( idx == kCCTouchBegan ) { + claimed = [handler.delegate ccTouchBegan:touch withEvent:event]; + if( claimed ) + [handler.claimedTouches addObject:touch]; + } + + // else (moved, ended, cancelled) + else if( [handler.claimedTouches containsObject:touch] ) { + claimed = YES; + if( handler.enabledSelectors & helper.type ) + [handler.delegate performSelector:helper.touchSel withObject:touch withObject:event]; + + if( helper.type & (kCCTouchSelectorCancelledBit | kCCTouchSelectorEndedBit) ) + [handler.claimedTouches removeObject:touch]; + } + + if( claimed && handler.swallowsTouches ) { + if( needsMutableSet ) + [mutableTouches removeObject:touch]; + break; + } + } + } + } + + // + // process standard handlers 2nd + // + if( standardHandlersCount > 0 && [mutableTouches count]>0 ) { + for( CCTouchHandler *handler in standardHandlers ) { + if( handler.enabledSelectors & helper.type ) + [handler.delegate performSelector:helper.touchesSel withObject:mutableTouches withObject:event]; + } + } + if( needsMutableSet ) + [mutableTouches release]; + + // + // Optimization. To prevent a [handlers copy] which is expensive + // the add/removes/quit is done after the iterations + // + locked = NO; + if( toRemove ) { + toRemove = NO; + for( id delegate in handlersToRemove ) + [self forceRemoveDelegate:delegate]; + [handlersToRemove removeAllObjects]; + } + if( toAdd ) { + toAdd = NO; + for( CCTouchHandler *handler in handlersToAdd ) { + Class targetedClass = [CCTargetedTouchHandler class]; + if( [handler isKindOfClass:targetedClass] ) + [self forceAddHandler:handler array:targetedHandlers]; + else + [self forceAddHandler:handler array:standardHandlers]; + } + [handlersToAdd removeAllObjects]; + } + if( toQuit ) { + toQuit = NO; + [self forceRemoveAllDelegates]; + } +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + if( dispatchEvents ) + [self touches:touches withEvent:event withTouchType:kCCTouchBegan]; +} +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + if( dispatchEvents ) + [self touches:touches withEvent:event withTouchType:kCCTouchMoved]; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + if( dispatchEvents ) + [self touches:touches withEvent:event withTouchType:kCCTouchEnded]; +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + if( dispatchEvents ) + [self touches:touches withEvent:event withTouchType:kCCTouchCancelled]; +} +@end + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED diff --git a/libs/cocos2d/Platforms/iOS/CCTouchHandler.h b/libs/cocos2d/Platforms/iOS/CCTouchHandler.h new file mode 100644 index 0000000..31a3e36 --- /dev/null +++ b/libs/cocos2d/Platforms/iOS/CCTouchHandler.h @@ -0,0 +1,93 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Valentin Milea + * + * 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. + * + */ + +// Only compile this code on iOS. These files should NOT be included on your Mac project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +/* + * This file contains the delegates of the touches + * There are 2 possible delegates: + * - CCStandardTouchHandler: propagates all the events at once + * - CCTargetedTouchHandler: propagates 1 event at the time + */ + +#import "CCTouchDelegateProtocol.h" +#import "CCTouchDispatcher.h" + +/** + CCTouchHandler + Object than contains the delegate and priority of the event handler. +*/ +@interface CCTouchHandler : NSObject { + id delegate; + int priority; + ccTouchSelectorFlag enabledSelectors_; +} + +/** delegate */ +@property(nonatomic, readwrite, retain) id delegate; +/** priority */ +@property(nonatomic, readwrite) int priority; // default 0 +/** enabled selectors */ +@property(nonatomic,readwrite) ccTouchSelectorFlag enabledSelectors; + +/** allocates a TouchHandler with a delegate and a priority */ ++ (id)handlerWithDelegate:(id)aDelegate priority:(int)priority; +/** initializes a TouchHandler with a delegate and a priority */ +- (id)initWithDelegate:(id)aDelegate priority:(int)priority; +@end + +/** CCStandardTouchHandler + It forwardes each event to the delegate. + */ +@interface CCStandardTouchHandler : CCTouchHandler +{ +} +@end + +/** + CCTargetedTouchHandler + Object than contains the claimed touches and if it swallos touches. + Used internally by TouchDispatcher + */ +@interface CCTargetedTouchHandler : CCTouchHandler { + BOOL swallowsTouches; + NSMutableSet *claimedTouches; +} +/** whether or not the touches are swallowed */ +@property(nonatomic, readwrite) BOOL swallowsTouches; // default NO +/** MutableSet that contains the claimed touches */ +@property(nonatomic, readonly) NSMutableSet *claimedTouches; + +/** allocates a TargetedTouchHandler with a delegate, a priority and whether or not it swallows touches or not */ ++ (id)handlerWithDelegate:(id) aDelegate priority:(int)priority swallowsTouches:(BOOL)swallowsTouches; +/** initializes a TargetedTouchHandler with a delegate, a priority and whether or not it swallows touches or not */ +- (id)initWithDelegate:(id) aDelegate priority:(int)priority swallowsTouches:(BOOL)swallowsTouches; + +@end + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED diff --git a/libs/cocos2d/Platforms/iOS/CCTouchHandler.m b/libs/cocos2d/Platforms/iOS/CCTouchHandler.m new file mode 100644 index 0000000..a52103b --- /dev/null +++ b/libs/cocos2d/Platforms/iOS/CCTouchHandler.m @@ -0,0 +1,135 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Valentin Milea + * + * 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. + * + */ + + +// Only compile this code on iOS. These files should NOT be included on your Mac project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +/* + * This file contains the delegates of the touches + * There are 2 possible delegates: + * - CCStandardTouchHandler: propagates all the events at once + * - CCTargetedTouchHandler: propagates 1 event at the time + */ + +#import "CCTouchHandler.h" +#import "../../ccMacros.h" + +#pragma mark - +#pragma mark TouchHandler +@implementation CCTouchHandler + +@synthesize delegate, priority; +@synthesize enabledSelectors=enabledSelectors_; + ++ (id)handlerWithDelegate:(id) aDelegate priority:(int)aPriority +{ + return [[[self alloc] initWithDelegate:aDelegate priority:aPriority] autorelease]; +} + +- (id)initWithDelegate:(id) aDelegate priority:(int)aPriority +{ + NSAssert(aDelegate != nil, @"Touch delegate may not be nil"); + + if ((self = [super init])) { + self.delegate = aDelegate; + priority = aPriority; + enabledSelectors_ = 0; + } + + return self; +} + +- (void)dealloc { + CCLOGINFO(@"cocos2d: deallocing %@", self); + [delegate release]; + [super dealloc]; +} +@end + +#pragma mark - +#pragma mark StandardTouchHandler +@implementation CCStandardTouchHandler +-(id) initWithDelegate:(id)del priority:(int)pri +{ + if( (self=[super initWithDelegate:del priority:pri]) ) { + if( [del respondsToSelector:@selector(ccTouchesBegan:withEvent:)] ) + enabledSelectors_ |= kCCTouchSelectorBeganBit; + if( [del respondsToSelector:@selector(ccTouchesMoved:withEvent:)] ) + enabledSelectors_ |= kCCTouchSelectorMovedBit; + if( [del respondsToSelector:@selector(ccTouchesEnded:withEvent:)] ) + enabledSelectors_ |= kCCTouchSelectorEndedBit; + if( [del respondsToSelector:@selector(ccTouchesCancelled:withEvent:)] ) + enabledSelectors_ |= kCCTouchSelectorCancelledBit; + } + return self; +} +@end + +#pragma mark - +#pragma mark TargetedTouchHandler + +@interface CCTargetedTouchHandler (private) +-(void) updateKnownTouches:(NSMutableSet *)touches withEvent:(UIEvent *)event selector:(SEL)selector unclaim:(BOOL)doUnclaim; +@end + +@implementation CCTargetedTouchHandler + +@synthesize swallowsTouches, claimedTouches; + ++ (id)handlerWithDelegate:(id)aDelegate priority:(int)priority swallowsTouches:(BOOL)swallow +{ + return [[[self alloc] initWithDelegate:aDelegate priority:priority swallowsTouches:swallow] autorelease]; +} + +- (id)initWithDelegate:(id)aDelegate priority:(int)aPriority swallowsTouches:(BOOL)swallow +{ + if ((self = [super initWithDelegate:aDelegate priority:aPriority])) { + claimedTouches = [[NSMutableSet alloc] initWithCapacity:2]; + swallowsTouches = swallow; + + if( [aDelegate respondsToSelector:@selector(ccTouchBegan:withEvent:)] ) + enabledSelectors_ |= kCCTouchSelectorBeganBit; + if( [aDelegate respondsToSelector:@selector(ccTouchMoved:withEvent:)] ) + enabledSelectors_ |= kCCTouchSelectorMovedBit; + if( [aDelegate respondsToSelector:@selector(ccTouchEnded:withEvent:)] ) + enabledSelectors_ |= kCCTouchSelectorEndedBit; + if( [aDelegate respondsToSelector:@selector(ccTouchCancelled:withEvent:)] ) + enabledSelectors_ |= kCCTouchSelectorCancelledBit; + } + + return self; +} + +- (void)dealloc { + [claimedTouches release]; + [super dealloc]; +} +@end + + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED \ No newline at end of file diff --git a/libs/cocos2d/Platforms/iOS/EAGLView.h b/libs/cocos2d/Platforms/iOS/EAGLView.h new file mode 100755 index 0000000..fd41c5e --- /dev/null +++ b/libs/cocos2d/Platforms/iOS/EAGLView.h @@ -0,0 +1,155 @@ +/* + +===== IMPORTANT ===== + +This is sample code demonstrating API, technology or techniques in development. +Although this sample code has been reviewed for technical accuracy, it is not +final. Apple is supplying this information to help you plan for the adoption of +the technologies and programming interfaces described herein. This information +is subject to change, and software implemented based on this sample code should +be tested with final operating system software and final documentation. Newer +versions of this sample code may be provided with future seeds of the API or +technology. For information about updates to this and other developer +documentation, view the New & Updated sidebars in subsequent documentation +seeds. + +===================== + +File: EAGLView.h +Abstract: Convenience class that wraps the CAEAGLLayer from CoreAnimation into a +UIView subclass. + +Version: 1.3 + +Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. +("Apple") in consideration of your agreement to the following terms, and your +use, installation, modification or redistribution of this Apple software +constitutes acceptance of these terms. If you do not agree with these terms, +please do not use, install, modify or redistribute this Apple software. + +In consideration of your agreement to abide by the following terms, and subject +to these terms, Apple grants you a personal, non-exclusive license, under +Apple's copyrights in this original Apple software (the "Apple Software"), to +use, reproduce, modify and redistribute the Apple Software, with or without +modifications, in source and/or binary forms; provided that if you redistribute +the Apple Software in its entirety and without modifications, you must retain +this notice and the following text and disclaimers in all such redistributions +of the Apple Software. +Neither the name, trademarks, service marks or logos of Apple Inc. may be used +to endorse or promote products derived from the Apple Software without specific +prior written permission from Apple. Except as expressly stated in this notice, +no other rights or licenses, express or implied, are granted by Apple herein, +including but not limited to any patent rights that may be infringed by your +derivative works or by other works in which the Apple Software may be +incorporated. + +The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO +WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED +WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN +COMBINATION WITH YOUR PRODUCTS. + +IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR +DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF +CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Copyright (C) 2008 Apple Inc. All Rights Reserved. + +*/ + +// Only compile this code on iOS. These files should NOT be included on your Mac project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +#import +#import +#import +#import +#import + +#import "ESRenderer.h" + +//CLASSES: + +@class EAGLView; +@class EAGLSharegroup; + +//PROTOCOLS: + +@protocol EAGLTouchDelegate +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; +@end + +//CLASS INTERFACE: + +/** EAGLView Class. + * This class wraps the CAEAGLLayer from CoreAnimation into a convenient UIView subclass. + * The view content is basically an EAGL surface you render your OpenGL scene into. + * Note that setting the view non-opaque will only work if the EAGL surface has an alpha channel. + */ +@interface EAGLView : UIView +{ + id renderer_; + EAGLContext *context_; // weak ref + + NSString *pixelformat_; + GLuint depthFormat_; + BOOL preserveBackbuffer_; + + CGSize size_; + BOOL discardFramebufferSupported_; + id touchDelegate_; + + //fsaa addition + BOOL multisampling_; + unsigned int requestedSamples_; +} + +/** creates an initializes an EAGLView with a frame and 0-bit depth buffer, and a RGB565 color buffer. */ ++ (id) viewWithFrame:(CGRect)frame; +/** creates an initializes an EAGLView with a frame, a color buffer format, and 0-bit depth buffer. */ ++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format; +/** creates an initializes an EAGLView with a frame, a color buffer format, and a depth buffer. */ ++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth; +/** creates an initializes an EAGLView with a frame, a color buffer format, a depth buffer format, a sharegroup, and multisamping */ ++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained sharegroup:(EAGLSharegroup*)sharegroup multiSampling:(BOOL)multisampling numberOfSamples:(unsigned int)samples; + +/** Initializes an EAGLView with a frame and 0-bit depth buffer, and a RGB565 color buffer */ +- (id) initWithFrame:(CGRect)frame; //These also set the current context +/** Initializes an EAGLView with a frame, a color buffer format, and 0-bit depth buffer */ +- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format; +/** Initializes an EAGLView with a frame, a color buffer format, a depth buffer format, a sharegroup and multisampling support */ +- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained sharegroup:(EAGLSharegroup*)sharegroup multiSampling:(BOOL)sampling numberOfSamples:(unsigned int)nSamples; + +/** pixel format: it could be RGBA8 (32-bit) or RGB565 (16-bit) */ +@property(nonatomic,readonly) NSString* pixelFormat; +/** depth format of the render buffer: 0, 16 or 24 bits*/ +@property(nonatomic,readonly) GLuint depthFormat; + +/** returns surface size in pixels */ +@property(nonatomic,readonly) CGSize surfaceSize; + +/** OpenGL context */ +@property(nonatomic,readonly) EAGLContext *context; + +@property(nonatomic,readwrite) BOOL multiSampling; + +/** touch delegate */ +@property(nonatomic,readwrite,assign) id touchDelegate; + +/** EAGLView uses double-buffer. This method swaps the buffers */ +-(void) swapBuffers; + +- (CGPoint) convertPointFromViewToSurface:(CGPoint)point; +- (CGRect) convertRectFromViewToSurface:(CGRect)rect; +@end + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED \ No newline at end of file diff --git a/libs/cocos2d/Platforms/iOS/EAGLView.m b/libs/cocos2d/Platforms/iOS/EAGLView.m new file mode 100755 index 0000000..39dcdb6 --- /dev/null +++ b/libs/cocos2d/Platforms/iOS/EAGLView.m @@ -0,0 +1,342 @@ +/* + +===== IMPORTANT ===== + +This is sample code demonstrating API, technology or techniques in development. +Although this sample code has been reviewed for technical accuracy, it is not +final. Apple is supplying this information to help you plan for the adoption of +the technologies and programming interfaces described herein. This information +is subject to change, and software implemented based on this sample code should +be tested with final operating system software and final documentation. Newer +versions of this sample code may be provided with future seeds of the API or +technology. For information about updates to this and other developer +documentation, view the New & Updated sidebars in subsequent documentation +seeds. + +===================== + +File: EAGLView.m +Abstract: Convenience class that wraps the CAEAGLLayer from CoreAnimation into a +UIView subclass. + +Version: 1.3 + +Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. +("Apple") in consideration of your agreement to the following terms, and your +use, installation, modification or redistribution of this Apple software +constitutes acceptance of these terms. If you do not agree with these terms, +please do not use, install, modify or redistribute this Apple software. + +In consideration of your agreement to abide by the following terms, and subject +to these terms, Apple grants you a personal, non-exclusive license, under +Apple's copyrights in this original Apple software (the "Apple Software"), to +use, reproduce, modify and redistribute the Apple Software, with or without +modifications, in source and/or binary forms; provided that if you redistribute +the Apple Software in its entirety and without modifications, you must retain +this notice and the following text and disclaimers in all such redistributions +of the Apple Software. +Neither the name, trademarks, service marks or logos of Apple Inc. may be used +to endorse or promote products derived from the Apple Software without specific +prior written permission from Apple. Except as expressly stated in this notice, +no other rights or licenses, express or implied, are granted by Apple herein, +including but not limited to any patent rights that may be infringed by your +derivative works or by other works in which the Apple Software may be +incorporated. + +The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO +WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED +WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN +COMBINATION WITH YOUR PRODUCTS. + +IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR +DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF +CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Copyright (C) 2008 Apple Inc. All Rights Reserved. + +*/ + +// Only compile this code on iOS. These files should NOT be included on your Mac project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +#import + +#import "EAGLView.h" +#import "ES1Renderer.h" +#import "../../CCDirector.h" +#import "../../ccMacros.h" +#import "../../CCConfiguration.h" +#import "../../Support/OpenGL_Internal.h" + + +//CLASS IMPLEMENTATIONS: + +@interface EAGLView (Private) +- (BOOL) setupSurfaceWithSharegroup:(EAGLSharegroup*)sharegroup; +- (unsigned int) convertPixelFormat:(NSString*) pixelFormat; +@end + +@implementation EAGLView + +@synthesize surfaceSize=size_; +@synthesize pixelFormat=pixelformat_, depthFormat=depthFormat_; +@synthesize touchDelegate=touchDelegate_; +@synthesize context=context_; +@synthesize multiSampling=multiSampling_; + ++ (Class) layerClass +{ + return [CAEAGLLayer class]; +} + ++ (id) viewWithFrame:(CGRect)frame +{ + return [[[self alloc] initWithFrame:frame] autorelease]; +} + ++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format +{ + return [[[self alloc] initWithFrame:frame pixelFormat:format] autorelease]; +} + ++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth +{ + return [[[self alloc] initWithFrame:frame pixelFormat:format depthFormat:depth preserveBackbuffer:NO sharegroup:nil multiSampling:NO numberOfSamples:0] autorelease]; +} + ++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained sharegroup:(EAGLSharegroup*)sharegroup multiSampling:(BOOL)multisampling numberOfSamples:(unsigned int)samples +{ + return [[[self alloc] initWithFrame:frame pixelFormat:format depthFormat:depth preserveBackbuffer:retained sharegroup:sharegroup multiSampling:multisampling numberOfSamples:samples] autorelease]; +} + +- (id) initWithFrame:(CGRect)frame +{ + return [self initWithFrame:frame pixelFormat:kEAGLColorFormatRGB565 depthFormat:0 preserveBackbuffer:NO sharegroup:nil multiSampling:NO numberOfSamples:0]; +} + +- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format +{ + return [self initWithFrame:frame pixelFormat:format depthFormat:0 preserveBackbuffer:NO sharegroup:nil multiSampling:NO numberOfSamples:0]; +} + +- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained sharegroup:(EAGLSharegroup*)sharegroup multiSampling:(BOOL)sampling numberOfSamples:(unsigned int)nSamples +{ + if((self = [super initWithFrame:frame])) + { + pixelformat_ = format; + depthFormat_ = depth; + multiSampling_ = sampling; + requestedSamples_ = nSamples; + preserveBackbuffer_ = retained; + + if( ! [self setupSurfaceWithSharegroup:sharegroup] ) { + [self release]; + return nil; + } + } + + return self; +} + +-(id) initWithCoder:(NSCoder *)aDecoder +{ + if( (self = [super initWithCoder:aDecoder]) ) { + + CAEAGLLayer* eaglLayer = (CAEAGLLayer*)[self layer]; + + pixelformat_ = kEAGLColorFormatRGB565; + depthFormat_ = 0; // GL_DEPTH_COMPONENT24_OES; + multiSampling_= NO; + requestedSamples_ = 0; + size_ = [eaglLayer bounds].size; + + if( ! [self setupSurfaceWithSharegroup:nil] ) { + [self release]; + return nil; + } + } + + return self; +} + +-(BOOL) setupSurfaceWithSharegroup:(EAGLSharegroup*)sharegroup +{ + CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer; + + eaglLayer.opaque = YES; + eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithBool:preserveBackbuffer_], kEAGLDrawablePropertyRetainedBacking, + pixelformat_, kEAGLDrawablePropertyColorFormat, nil]; + + + renderer_ = [[ES1Renderer alloc] initWithDepthFormat:depthFormat_ + withPixelFormat:[self convertPixelFormat:pixelformat_] + withSharegroup:sharegroup + withMultiSampling:multiSampling_ + withNumberOfSamples:requestedSamples_]; + if (!renderer_) + return NO; + + context_ = [renderer_ context]; + [context_ renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:eaglLayer]; + + discardFramebufferSupported_ = [[CCConfiguration sharedConfiguration] supportsDiscardFramebuffer]; + + return YES; +} + +- (void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + + + [renderer_ release]; + [super dealloc]; +} + +- (void) layoutSubviews +{ + [renderer_ resizeFromLayer:(CAEAGLLayer*)self.layer]; + size_ = [renderer_ backingSize]; + + // Issue #914 #924 + CCDirector *director = [CCDirector sharedDirector]; + [director reshapeProjection:size_]; + + // Avoid flicker. Issue #350 + [director performSelectorOnMainThread:@selector(drawScene) withObject:nil waitUntilDone:YES]; +} + +- (void) swapBuffers +{ + // IMPORTANT: + // - preconditions + // -> context_ MUST be the OpenGL context + // -> renderbuffer_ must be the the RENDER BUFFER + +#ifdef __IPHONE_4_0 + + if (multiSampling_) + { + /* Resolve from msaaFramebuffer to resolveFramebuffer */ + //glDisable(GL_SCISSOR_TEST); + glBindFramebufferOES(GL_READ_FRAMEBUFFER_APPLE, [renderer_ msaaFrameBuffer]); + glBindFramebufferOES(GL_DRAW_FRAMEBUFFER_APPLE, [renderer_ defaultFrameBuffer]); + glResolveMultisampleFramebufferAPPLE(); + } + + if( discardFramebufferSupported_) + { + if (multiSampling_) + { + if (depthFormat_) + { + GLenum attachments[] = {GL_COLOR_ATTACHMENT0_OES, GL_DEPTH_ATTACHMENT_OES}; + glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 2, attachments); + } + else + { + GLenum attachments[] = {GL_COLOR_ATTACHMENT0_OES}; + glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 1, attachments); + } + + glBindRenderbufferOES(GL_RENDERBUFFER_OES, [renderer_ colorRenderBuffer]); + + } + + // not MSAA + else if (depthFormat_ ) { + GLenum attachments[] = { GL_DEPTH_ATTACHMENT_OES}; + glDiscardFramebufferEXT(GL_FRAMEBUFFER_OES, 1, attachments); + } + } + +#endif // __IPHONE_4_0 + + if(![context_ presentRenderbuffer:GL_RENDERBUFFER_OES]) + CCLOG(@"cocos2d: Failed to swap renderbuffer in %s\n", __FUNCTION__); + +#if COCOS2D_DEBUG + CHECK_GL_ERROR(); +#endif + + // We can safely re-bind the framebuffer here, since this will be the + // 1st instruction of the new main loop + if( multiSampling_ ) + glBindFramebufferOES(GL_FRAMEBUFFER_OES, [renderer_ msaaFrameBuffer]); +} + +- (unsigned int) convertPixelFormat:(NSString*) pixelFormat +{ + // define the pixel format + GLenum pFormat; + + + if([pixelFormat isEqualToString:@"EAGLColorFormat565"]) + pFormat = GL_RGB565_OES; + else + pFormat = GL_RGBA8_OES; + + return pFormat; +} + +#pragma mark EAGLView - Point conversion + +- (CGPoint) convertPointFromViewToSurface:(CGPoint)point +{ + CGRect bounds = [self bounds]; + + return CGPointMake((point.x - bounds.origin.x) / bounds.size.width * size_.width, (point.y - bounds.origin.y) / bounds.size.height * size_.height); +} + +- (CGRect) convertRectFromViewToSurface:(CGRect)rect +{ + CGRect bounds = [self bounds]; + + return CGRectMake((rect.origin.x - bounds.origin.x) / bounds.size.width * size_.width, (rect.origin.y - bounds.origin.y) / bounds.size.height * size_.height, rect.size.width / bounds.size.width * size_.width, rect.size.height / bounds.size.height * size_.height); +} + +// Pass the touches to the superview +#pragma mark EAGLView - Touch Delegate + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + if(touchDelegate_) + { + [touchDelegate_ touchesBegan:touches withEvent:event]; + } +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + if(touchDelegate_) + { + [touchDelegate_ touchesMoved:touches withEvent:event]; + } +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + if(touchDelegate_) + { + [touchDelegate_ touchesEnded:touches withEvent:event]; + } +} +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + if(touchDelegate_) + { + [touchDelegate_ touchesCancelled:touches withEvent:event]; + } +} + +@end + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED \ No newline at end of file diff --git a/libs/cocos2d/Platforms/iOS/ES1Renderer.h b/libs/cocos2d/Platforms/iOS/ES1Renderer.h new file mode 100755 index 0000000..d5ce292 --- /dev/null +++ b/libs/cocos2d/Platforms/iOS/ES1Renderer.h @@ -0,0 +1,71 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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. + * + * + * File autogenerated with Xcode. Adapted for cocos2d needs. + */ + +// Only compile this code on iOS. These files should NOT be included on your Mac project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + + +#import "ESRenderer.h" + +#import +#import + +@interface ES1Renderer : NSObject +{ + // The pixel dimensions of the CAEAGLLayer + GLint backingWidth_; + GLint backingHeight_; + + unsigned int samplesToUse_; + BOOL multiSampling_; + + unsigned int depthFormat_; + unsigned int pixelFormat_; + + // The OpenGL ES names for the framebuffer and renderbuffer used to render to this view + GLuint defaultFramebuffer_; + GLuint colorRenderbuffer_; + GLuint depthBuffer_; + + + //buffers for MSAA + GLuint msaaFramebuffer_; + GLuint msaaColorbuffer_; + + EAGLContext *context_; +} + +/** EAGLContext */ +@property (nonatomic,readonly) EAGLContext* context; + +- (BOOL)resizeFromLayer:(CAEAGLLayer *)layer; + +@end + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED diff --git a/libs/cocos2d/Platforms/iOS/ES1Renderer.m b/libs/cocos2d/Platforms/iOS/ES1Renderer.m new file mode 100755 index 0000000..73a5814 --- /dev/null +++ b/libs/cocos2d/Platforms/iOS/ES1Renderer.m @@ -0,0 +1,252 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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. + * + * + * File autogenerated with Xcode. Adapted for cocos2d needs. + */ + +// Only compile this code on iOS. These files should NOT be included on your Mac project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +#import "ES1Renderer.h" +#import "../../Support/OpenGL_Internal.h" +#import "../../ccMacros.h" + + +@interface ES1Renderer (private) + +- (GLenum) convertPixelFormat:(int) pixelFormat; + +@end + + +@implementation ES1Renderer + +@synthesize context=context_; + +- (id) initWithDepthFormat:(unsigned int)depthFormat withPixelFormat:(unsigned int)pixelFormat withSharegroup:(EAGLSharegroup*)sharegroup withMultiSampling:(BOOL) multiSampling withNumberOfSamples:(unsigned int) requestedSamples +{ + if ((self = [super init])) + { + if ( sharegroup == nil ) + { + context_ = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; + } + else + { + context_ = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1 sharegroup:sharegroup]; + } + + if (!context_ || ![EAGLContext setCurrentContext:context_]) + { + [self release]; + return nil; + } + + // Create default framebuffer object. The backing will be allocated for the current layer in -resizeFromLayer + glGenFramebuffersOES(1, &defaultFramebuffer_); + NSAssert( defaultFramebuffer_, @"Can't create default frame buffer"); + glGenRenderbuffersOES(1, &colorRenderbuffer_); + NSAssert( colorRenderbuffer_, @"Can't create default render buffer"); + + glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer_); + glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_); + glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, colorRenderbuffer_); + + depthFormat_ = depthFormat; + + if( depthFormat_ ) { +// glGenRenderbuffersOES(1, &depthBuffer_); +// glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthBuffer_); +// glRenderbufferStorageOES(GL_RENDERBUFFER_OES, depthFormat_, 100, 100); +// glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthBuffer_); + + // default buffer +// glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_); + } + + pixelFormat_ = pixelFormat; + multiSampling_ = multiSampling; + if (multiSampling_) + { + GLint maxSamplesAllowed; + glGetIntegerv(GL_MAX_SAMPLES_APPLE, &maxSamplesAllowed); + samplesToUse_ = MIN(maxSamplesAllowed,requestedSamples); + + /* Create the MSAA framebuffer (offscreen) */ + glGenFramebuffersOES(1, &msaaFramebuffer_); + glBindFramebufferOES(GL_FRAMEBUFFER_OES, msaaFramebuffer_); + + } + + CHECK_GL_ERROR(); + } + + return self; +} + +- (BOOL)resizeFromLayer:(CAEAGLLayer *)layer +{ + // Allocate color buffer backing based on the current layer size + + glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_); + + if (![context_ renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:layer]) + { + CCLOG(@"failed to call context"); + } + + glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth_); + glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight_); + + CCLOG(@"cocos2d: surface size: %dx%d", (int)backingWidth_, (int)backingHeight_); + + if (multiSampling_) + { + /* Create the offscreen MSAA color buffer. + After rendering, the contents of this will be blitted into ColorRenderbuffer */ + + //msaaFrameBuffer needs to be binded + glBindFramebufferOES(GL_FRAMEBUFFER_OES, msaaFramebuffer_); + glGenRenderbuffersOES(1, &msaaColorbuffer_); + glBindRenderbufferOES(GL_RENDERBUFFER_OES, msaaColorbuffer_); + glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, samplesToUse_,pixelFormat_ , backingWidth_, backingHeight_); + glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, msaaColorbuffer_); + + if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) + { + CCLOG(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES)); + return NO; + } + } + + if (depthFormat_) + { + if( ! depthBuffer_ ) + glGenRenderbuffersOES(1, &depthBuffer_); + + glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthBuffer_); + if( multiSampling_ ) + glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, samplesToUse_, depthFormat_,backingWidth_, backingHeight_); + else + glRenderbufferStorageOES(GL_RENDERBUFFER_OES, depthFormat_, backingWidth_, backingHeight_); + glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthBuffer_); + + // bind color buffer + glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_); + } + + glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer_); + + if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) + { + CCLOG(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES)); + return NO; + } + + CHECK_GL_ERROR(); + + return YES; +} + +-(CGSize) backingSize +{ + return CGSizeMake( backingWidth_, backingHeight_); +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | size = %ix%i>", [self class], self, backingWidth_, backingHeight_]; +} + + +- (void)dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + + // Tear down GL + if(defaultFramebuffer_) + { + glDeleteFramebuffersOES(1, &defaultFramebuffer_); + defaultFramebuffer_ = 0; + } + + if(colorRenderbuffer_) + { + glDeleteRenderbuffersOES(1, &colorRenderbuffer_); + colorRenderbuffer_ = 0; + } + + if( depthBuffer_ ) + { + glDeleteRenderbuffersOES(1, &depthBuffer_); + depthBuffer_ = 0; + } + + if ( msaaColorbuffer_) + { + glDeleteRenderbuffersOES(1, &msaaColorbuffer_); + msaaColorbuffer_ = 0; + } + + if ( msaaFramebuffer_) + { + glDeleteRenderbuffersOES(1, &msaaFramebuffer_); + msaaFramebuffer_ = 0; + } + + // Tear down context + if ([EAGLContext currentContext] == context_) + [EAGLContext setCurrentContext:nil]; + + [context_ release]; + context_ = nil; + + [super dealloc]; +} + +- (unsigned int) colorRenderBuffer +{ + return colorRenderbuffer_; +} + +- (unsigned int) defaultFrameBuffer +{ + return defaultFramebuffer_; +} + +- (unsigned int) msaaFrameBuffer +{ + return msaaFramebuffer_; +} + +- (unsigned int) msaaColorBuffer +{ + return msaaColorbuffer_; +} + +@end + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED diff --git a/libs/cocos2d/Platforms/iOS/ESRenderer.h b/libs/cocos2d/Platforms/iOS/ESRenderer.h new file mode 100755 index 0000000..ff54ccb --- /dev/null +++ b/libs/cocos2d/Platforms/iOS/ESRenderer.h @@ -0,0 +1,53 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Ricardo Quesada + * + * 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. + * + * + * File autogenerated with Xcode. Adapted for cocos2d needs. + */ + +// Only compile this code on iOS. These files should NOT be included on your Mac project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +#import + +#import +#import + +@protocol ESRenderer + +- (id) initWithDepthFormat:(unsigned int)depthFormat withPixelFormat:(unsigned int)pixelFormat withSharegroup:(EAGLSharegroup*)sharegroup withMultiSampling:(BOOL) multiSampling withNumberOfSamples:(unsigned int) requestedSamples; + +- (BOOL) resizeFromLayer:(CAEAGLLayer *)layer; + +- (EAGLContext*) context; +- (CGSize) backingSize; + +- (unsigned int) colorRenderBuffer; +- (unsigned int) defaultFrameBuffer; +- (unsigned int) msaaFrameBuffer; +- (unsigned int) msaaColorBuffer; +@end + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED diff --git a/libs/cocos2d/Platforms/iOS/glu.c b/libs/cocos2d/Platforms/iOS/glu.c new file mode 100755 index 0000000..2e00d5f --- /dev/null +++ b/libs/cocos2d/Platforms/iOS/glu.c @@ -0,0 +1,113 @@ +// +// cocos2d (incomplete) GLU implementation +// +// gluLookAt and gluPerspective from: +// http://jet.ro/creations (San Angeles Observation) +// +// + +// Only compile this code on iOS. These files should NOT be included on your Mac project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +#import +#import +#import "../../Support/OpenGL_Internal.h" +#include "glu.h" + +void gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar) +{ + GLfloat xmin, xmax, ymin, ymax; + + ymax = zNear * (GLfloat)tanf(fovy * (float)M_PI / 360); + ymin = -ymax; + xmin = ymin * aspect; + xmax = ymax * aspect; + + glFrustumf(xmin, xmax, + ymin, ymax, + zNear, zFar); +} + +void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez, + GLfloat centerx, GLfloat centery, GLfloat centerz, + GLfloat upx, GLfloat upy, GLfloat upz) +{ + GLfloat m[16]; + GLfloat x[3], y[3], z[3]; + GLfloat mag; + + /* Make rotation matrix */ + + /* Z vector */ + z[0] = eyex - centerx; + z[1] = eyey - centery; + z[2] = eyez - centerz; + mag = (float)sqrtf(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]); + if (mag) { + z[0] /= mag; + z[1] /= mag; + z[2] /= mag; + } + + /* Y vector */ + y[0] = upx; + y[1] = upy; + y[2] = upz; + + /* X vector = Y cross Z */ + x[0] = y[1] * z[2] - y[2] * z[1]; + x[1] = -y[0] * z[2] + y[2] * z[0]; + x[2] = y[0] * z[1] - y[1] * z[0]; + + /* Recompute Y = Z cross X */ + y[0] = z[1] * x[2] - z[2] * x[1]; + y[1] = -z[0] * x[2] + z[2] * x[0]; + y[2] = z[0] * x[1] - z[1] * x[0]; + + /* cross product gives area of parallelogram, which is < 1.0 for + * non-perpendicular unit-length vectors; so normalize x, y here + */ + + mag = (float)sqrtf(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]); + if (mag) { + x[0] /= mag; + x[1] /= mag; + x[2] /= mag; + } + + mag = (float)sqrtf(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]); + if (mag) { + y[0] /= mag; + y[1] /= mag; + y[2] /= mag; + } + +#define M(row,col) m[col*4+row] + M(0, 0) = x[0]; + M(0, 1) = x[1]; + M(0, 2) = x[2]; + M(0, 3) = 0.0f; + M(1, 0) = y[0]; + M(1, 1) = y[1]; + M(1, 2) = y[2]; + M(1, 3) = 0.0f; + M(2, 0) = z[0]; + M(2, 1) = z[1]; + M(2, 2) = z[2]; + M(2, 3) = 0.0f; + M(3, 0) = 0.0f; + M(3, 1) = 0.0f; + M(3, 2) = 0.0f; + M(3, 3) = 1.0f; +#undef M + + glMultMatrixf(m); + + + /* Translate Eye to Origin */ + glTranslatef(-eyex, -eyey, -eyez); +} + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED diff --git a/libs/cocos2d/Platforms/iOS/glu.h b/libs/cocos2d/Platforms/iOS/glu.h new file mode 100644 index 0000000..86dcac7 --- /dev/null +++ b/libs/cocos2d/Platforms/iOS/glu.h @@ -0,0 +1,29 @@ +// +// cocos2d GLU implementation +// +// implementation of GLU functions +// +#ifndef __COCOS2D_GLU_H +#define __COCOS2D_GLU_H + +// Only compile this code on iOS. These files should NOT be included on your Mac project. +// But in case they are included, it won't be compiled. +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +#import + +/** + @file + cocos2d OpenGL GLU implementation + */ + +/** OpenGL gluLookAt implementation */ +void gluLookAt(float eyeX, float eyeY, float eyeZ, float lookAtX, float lookAtY, float lookAtZ, float upX, float upY, float upZ); +/** OpenGL gluPerspective implementation */ +void gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar); + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED + +#endif /* __COCOS2D_GLU_H */ + diff --git a/libs/cocos2d/Support/CCArray.h b/libs/cocos2d/Support/CCArray.h new file mode 100644 index 0000000..8a5a7a8 --- /dev/null +++ b/libs/cocos2d/Support/CCArray.h @@ -0,0 +1,105 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 ForzeField Studios S.L. http://forzefield.com + * + * 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 "ccCArray.h" + + +/** A faster alternative of NSArray. + CCArray uses internally a c-array. + @since v0.99.4 + */ + + +/** @def CCARRAY_FOREACH + A convience macro to iterate over a CCArray using. It is faster than the "fast enumeration" interface. + @since v0.99.4 + */ + +#define CCARRAY_FOREACH(__array__, __object__) \ +if (__array__ && __array__->data->num > 0) \ +for(id *arr = __array__->data->arr, *end = __array__->data->arr + __array__->data->num-1; \ + arr <= end && ((__object__ = *arr) != nil || true); \ + arr++) + +@interface CCArray : NSObject +{ + @public ccArray *data; +} + ++ (id) array; ++ (id) arrayWithCapacity:(NSUInteger)capacity; ++ (id) arrayWithArray:(CCArray*)otherArray; ++ (id) arrayWithNSArray:(NSArray*)otherArray; + + +- (id) initWithCapacity:(NSUInteger)capacity; +- (id) initWithArray:(CCArray*)otherArray; +- (id) initWithNSArray:(NSArray*)otherArray; + + +// Querying an Array + +- (NSUInteger) count; +- (NSUInteger) capacity; +- (NSUInteger) indexOfObject:(id)object; +- (id) objectAtIndex:(NSUInteger)index; +- (BOOL) containsObject:(id)object; +- (id) randomObject; +- (id) lastObject; +- (NSArray*) getNSArray; + + +// Adding Objects + +- (void) addObject:(id)object; +- (void) addObjectsFromArray:(CCArray*)otherArray; +- (void) addObjectsFromNSArray:(NSArray*)otherArray; +- (void) insertObject:(id)object atIndex:(NSUInteger)index; + + +// Removing Objects + +- (void) removeLastObject; +- (void) removeObject:(id)object; +- (void) removeObjectAtIndex:(NSUInteger)index; +- (void) removeObjectsInArray:(CCArray*)otherArray; +- (void) removeAllObjects; +- (void) fastRemoveObject:(id)object; +- (void) fastRemoveObjectAtIndex:(NSUInteger)index; + + +// Rearranging Content + +- (void) exchangeObject:(id)object1 withObject:(id)object2; +- (void) exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2; + + +// Sending Messages to Elements + +- (void) makeObjectsPerformSelector:(SEL)aSelector; +- (void) makeObjectsPerformSelector:(SEL)aSelector withObject:(id)object; + + +@end diff --git a/libs/cocos2d/Support/CCArray.m b/libs/cocos2d/Support/CCArray.m new file mode 100644 index 0000000..e2517b6 --- /dev/null +++ b/libs/cocos2d/Support/CCArray.m @@ -0,0 +1,270 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 ForzeFied Studios S.L. http://forzefield.com + * + * 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 "CCArray.h" +#import "../ccMacros.h" + + +@implementation CCArray + ++ (id) array +{ + return [[[self alloc] init] autorelease]; +} + ++ (id) arrayWithCapacity:(NSUInteger)capacity +{ + return [[[self alloc] initWithCapacity:capacity] autorelease]; +} + ++ (id) arrayWithArray:(CCArray*)otherArray +{ + return [[(CCArray*)[self alloc] initWithArray:otherArray] autorelease]; +} + ++ (id) arrayWithNSArray:(NSArray*)otherArray +{ + return [[(CCArray*)[self alloc] initWithNSArray:otherArray] autorelease]; +} + +- (id) init +{ + self = [self initWithCapacity:2]; + return self; +} + +- (id) initWithCapacity:(NSUInteger)capacity +{ + self = [super init]; + if (self != nil) { + data = ccArrayNew(capacity); + } + return self; +} + +- (id) initWithArray:(CCArray*)otherArray +{ + self = [self initWithCapacity:otherArray->data->num]; + if (self != nil) { + [self addObjectsFromArray:otherArray]; + } + return self; +} + +- (id) initWithNSArray:(NSArray*)otherArray +{ + self = [self initWithCapacity:otherArray.count]; + if (self != nil) { + [self addObjectsFromNSArray:otherArray]; + } + return self; +} + +- (id) initWithCoder:(NSCoder*)coder +{ + self = [self initWithNSArray:[coder decodeObjectForKey:@"nsarray"]]; + return self; +} + + +#pragma mark Querying an Array + +- (NSUInteger) count +{ + return data->num; +} + +- (NSUInteger) capacity +{ + return data->max; +} + +- (NSUInteger) indexOfObject:(id)object +{ + return ccArrayGetIndexOfObject(data, object); +} + +- (id) objectAtIndex:(NSUInteger)index +{ + NSAssert2( index < data->num, @"index out of range in objectAtIndex(%d), index %i", data->num, index ); + + return data->arr[index]; +} + +- (BOOL) containsObject:(id)object +{ + return ccArrayContainsObject(data, object); +} + +- (id) lastObject +{ + if( data->num > 0 ) + return data->arr[data->num-1]; + return nil; +} + +- (id) randomObject +{ + if(data->num==0) return nil; + return data->arr[(int)(data->num*CCRANDOM_0_1())]; +} + +- (NSArray*) getNSArray +{ + return [NSArray arrayWithObjects:data->arr count:data->num]; +} + + +#pragma mark Adding Objects + +- (void) addObject:(id)object +{ + ccArrayAppendObjectWithResize(data, object); +} + +- (void) addObjectsFromArray:(CCArray*)otherArray +{ + ccArrayAppendArrayWithResize(data, otherArray->data); +} + +- (void) addObjectsFromNSArray:(NSArray*)otherArray +{ + ccArrayEnsureExtraCapacity(data, otherArray.count); + for(id object in otherArray) + ccArrayAppendObject(data, object); +} + +- (void) insertObject:(id)object atIndex:(NSUInteger)index +{ + ccArrayInsertObjectAtIndex(data, object, index); +} + + +#pragma mark Removing Objects + +- (void) removeObject:(id)object +{ + ccArrayRemoveObject(data, object); +} + +- (void) removeObjectAtIndex:(NSUInteger)index +{ + ccArrayRemoveObjectAtIndex(data, index); +} + +- (void) fastRemoveObject:(id)object +{ + ccArrayFastRemoveObject(data, object); +} + +- (void) fastRemoveObjectAtIndex:(NSUInteger)index +{ + ccArrayFastRemoveObjectAtIndex(data, index); +} + +- (void) removeObjectsInArray:(CCArray*)otherArray +{ + ccArrayRemoveArray(data, otherArray->data); +} + +- (void) removeLastObject +{ + NSAssert( data->num > 0, @"no objects added" ); + + ccArrayRemoveObjectAtIndex(data, data->num-1); +} + +- (void) removeAllObjects +{ + ccArrayRemoveAllObjects(data); +} + + +#pragma mark Rearranging Content + +- (void) exchangeObject:(id)object1 withObject:(id)object2 +{ + NSUInteger index1 = ccArrayGetIndexOfObject(data, object1); + if(index1 == NSNotFound) return; + NSUInteger index2 = ccArrayGetIndexOfObject(data, object2); + if(index2 == NSNotFound) return; + + ccArraySwapObjectsAtIndexes(data, index1, index2); +} + +- (void) exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2 +{ + ccArraySwapObjectsAtIndexes(data, index1, index2); +} + + +#pragma mark Sending Messages to Elements + +- (void) makeObjectsPerformSelector:(SEL)aSelector +{ + ccArrayMakeObjectsPerformSelector(data, aSelector); +} + +- (void) makeObjectsPerformSelector:(SEL)aSelector withObject:(id)object +{ + ccArrayMakeObjectsPerformSelectorWithObject(data, aSelector, object); +} + + +#pragma mark CCArray - NSFastEnumeration protocol + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len +{ + if(state->state == 1) return 0; + + state->mutationsPtr = (unsigned long *)self; + state->itemsPtr = &data->arr[0]; + state->state = 1; + return data->num; +} + + +#pragma mark CCArray - NSCopying protocol + +- (id)copyWithZone:(NSZone *)zone +{ + NSArray *nsArray = [self getNSArray]; + CCArray *newArray = [[[self class] allocWithZone:zone] initWithNSArray:nsArray]; + return newArray; +} + +- (void) encodeWithCoder:(NSCoder *)coder +{ + [coder encodeObject:[self getNSArray] forKey:@"nsarray"]; +} + +#pragma mark + +- (void) dealloc +{ + ccArrayFree(data); + [super dealloc]; +} + +@end diff --git a/libs/cocos2d/Support/CCFileUtils.h b/libs/cocos2d/Support/CCFileUtils.h new file mode 100644 index 0000000..d906c69 --- /dev/null +++ b/libs/cocos2d/Support/CCFileUtils.h @@ -0,0 +1,61 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 + + +/** Helper class to handle file operations */ +@interface CCFileUtils : NSObject +{ +} + +/** Returns the fullpath of an filename. + + If this method is when Retina Display is enabled, then the + Retina Display suffix will be appended to the file (See ccConfig.h). + + If the Retina Display image doesn't exist, then it will return the "non-Retina Display" image + + */ ++(NSString*) fullPathFromRelativePath:(NSString*) relPath; +@end + +/** loads a file into memory. + the caller should release the allocated buffer. + + @returns the size of the allocated buffer + @since v0.99.5 + */ +NSInteger ccLoadFileIntoMemory(const char *filename, unsigned char **out); + + +/** removes the HD suffix from a path + + @returns NSString * without the HD suffix + @since v0.99.5 + */ +NSString *ccRemoveHDSuffixFromFile( NSString *path ); + diff --git a/libs/cocos2d/Support/CCFileUtils.m b/libs/cocos2d/Support/CCFileUtils.m new file mode 100644 index 0000000..ff0be83 --- /dev/null +++ b/libs/cocos2d/Support/CCFileUtils.m @@ -0,0 +1,168 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 +#import "CCFileUtils.h" +#import "../CCConfiguration.h" +#import "../ccMacros.h" +#import "../ccConfig.h" + +static NSFileManager *__localFileManager=nil; + +// +NSInteger ccLoadFileIntoMemory(const char *filename, unsigned char **out) +{ + assert( out ); + assert( &*out ); + + size_t size = 0; + FILE *f = fopen(filename, "rb"); + if( !f ) { + *out = NULL; + return -1; + } + + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + + *out = malloc(size); + size_t read = fread(*out, 1, size, f); + if( read != size ) { + free(*out); + *out = NULL; + return -1; + } + + fclose(f); + + return size; +} + +NSString *ccRemoveHDSuffixFromFile( NSString *path ) +{ +#if CC_IS_RETINA_DISPLAY_SUPPORTED + + if( CC_CONTENT_SCALE_FACTOR() == 2 ) { + + NSString *name = [path lastPathComponent]; + + // check if path already has the suffix. + if( [name rangeOfString:CC_RETINA_DISPLAY_FILENAME_SUFFIX].location != NSNotFound ) { + + CCLOG(@"cocos2d: Filename(%@) contains %@ suffix. Removing it. See cocos2d issue #1040", path, CC_RETINA_DISPLAY_FILENAME_SUFFIX); + + NSString *newLastname = [name stringByReplacingOccurrencesOfString:CC_RETINA_DISPLAY_FILENAME_SUFFIX withString:@""]; + + NSString *pathWithoutLastname = [path stringByDeletingLastPathComponent]; + return [pathWithoutLastname stringByAppendingPathComponent:newLastname]; + } + } + +#endif // CC_IS_RETINA_DISPLAY_SUPPORTED + + return path; + +} + + +@implementation CCFileUtils + ++(void) initialize +{ + if( self == [CCFileUtils class] ) + __localFileManager = [[NSFileManager alloc] init]; +} + ++(NSString*) getDoubleResolutionImage:(NSString*)path +{ +#if CC_IS_RETINA_DISPLAY_SUPPORTED + + if( CC_CONTENT_SCALE_FACTOR() == 2 ) + { + + NSString *pathWithoutExtension = [path stringByDeletingPathExtension]; + NSString *name = [pathWithoutExtension lastPathComponent]; + + // check if path already has the suffix. + if( [name rangeOfString:CC_RETINA_DISPLAY_FILENAME_SUFFIX].location != NSNotFound ) { + + CCLOG(@"cocos2d: WARNING Filename(%@) already has the suffix %@. Using it.", name, CC_RETINA_DISPLAY_FILENAME_SUFFIX); + return path; + } + + + NSString *extension = [path pathExtension]; + + if( [extension isEqualToString:@"ccz"] || [extension isEqualToString:@"gz"] ) + { + // All ccz / gz files should be in the format filename.xxx.ccz + // so we need to pull off the .xxx part of the extension as well + extension = [NSString stringWithFormat:@"%@.%@", [pathWithoutExtension pathExtension], extension]; + pathWithoutExtension = [pathWithoutExtension stringByDeletingPathExtension]; + } + + + NSString *retinaName = [pathWithoutExtension stringByAppendingString:CC_RETINA_DISPLAY_FILENAME_SUFFIX]; + retinaName = [retinaName stringByAppendingPathExtension:extension]; + + if( [__localFileManager fileExistsAtPath:retinaName] ) + return retinaName; + + CCLOG(@"cocos2d: CCFileUtils: Warning HD file not found: %@", [retinaName lastPathComponent] ); + } + +#endif // CC_IS_RETINA_DISPLAY_SUPPORTED + + return path; +} + ++(NSString*) fullPathFromRelativePath:(NSString*) relPath +{ + NSAssert(relPath != nil, @"CCFileUtils: Invalid path"); + + NSString *fullpath = nil; + + // only if it is not an absolute path + if( ! [relPath isAbsolutePath] ) + { + NSString *file = [relPath lastPathComponent]; + NSString *imageDirectory = [relPath stringByDeletingLastPathComponent]; + + fullpath = [[NSBundle mainBundle] pathForResource:file + ofType:nil + inDirectory:imageDirectory]; + } + + if (fullpath == nil) + fullpath = relPath; + + fullpath = [self getDoubleResolutionImage:fullpath]; + + return fullpath; +} + +@end diff --git a/libs/cocos2d/Support/CCProfiling.h b/libs/cocos2d/Support/CCProfiling.h new file mode 100644 index 0000000..b241fb9 --- /dev/null +++ b/libs/cocos2d/Support/CCProfiling.h @@ -0,0 +1,53 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Stuart Carnie + * + * 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 +#import + +@class CCProfilingTimer; + +@interface CCProfiler : NSObject { + NSMutableArray* activeTimers; +} + ++ (CCProfiler*)sharedProfiler; ++ (CCProfilingTimer*)timerWithName:(NSString*)timerName andInstance:(id)instance; ++ (void)releaseTimer:(CCProfilingTimer*)timer; +- (void)displayTimers; + +@end + + +@interface CCProfilingTimer : NSObject { + NSString* name; + struct timeval startTime; + double averageTime; +} + +@end + +extern void CCProfilingBeginTimingBlock(CCProfilingTimer* timer); +extern void CCProfilingEndTimingBlock(CCProfilingTimer* timer); diff --git a/libs/cocos2d/Support/CCProfiling.m b/libs/cocos2d/Support/CCProfiling.m new file mode 100644 index 0000000..13c8c81 --- /dev/null +++ b/libs/cocos2d/Support/CCProfiling.m @@ -0,0 +1,117 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Stuart Carnie + * + * 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 "../ccConfig.h" + +#if CC_ENABLE_PROFILERS + +#import "CCProfiling.h" + +@interface CCProfilingTimer() +- (id)initWithName:(NSString*)timerName andInstance:(id)instance; +@end + +@implementation CCProfiler + +static CCProfiler* g_sharedProfiler; + ++ (CCProfiler*)sharedProfiler { + if (!g_sharedProfiler) + g_sharedProfiler = [[CCProfiler alloc] init]; + + return g_sharedProfiler; +} + ++ (CCProfilingTimer*)timerWithName:(NSString*)timerName andInstance:(id)instance { + CCProfiler* p = [CCProfiler sharedProfiler]; + CCProfilingTimer* t = [[CCProfilingTimer alloc] initWithName:timerName andInstance:instance]; + [p->activeTimers addObject:t]; + [t release]; + return t; +} + ++ (void)releaseTimer:(CCProfilingTimer*)timer { + CCProfiler* p = [CCProfiler sharedProfiler]; + [p->activeTimers removeObject:timer]; +} + +- (id)init { + if (!(self = [super init])) return nil; + + activeTimers = [[NSMutableArray alloc] init]; + + return self; +} + +- (void)dealloc { + [activeTimers release]; + [super dealloc]; +} + +- (void)displayTimers { + for (id timer in activeTimers) { + printf("%s\n", [[timer description] cStringUsingEncoding:[NSString defaultCStringEncoding]]); + } +} + +@end + +@implementation CCProfilingTimer + +- (id)initWithName:(NSString*)timerName andInstance:(id)instance { + if (!(self = [super init])) return nil; + + name = [[NSString stringWithFormat:@"%@ (0x%.8x)", timerName, instance] retain]; + + return self; +} + +- (void)dealloc { + [name release]; + [super dealloc]; +} + +- (NSString*)description { + return [NSString stringWithFormat:@"%@ : avg time, %fms", name, averageTime]; +} + +void CCProfilingBeginTimingBlock(CCProfilingTimer* timer) { + gettimeofday(&timer->startTime, NULL); +} + +typedef unsigned int uint32; +void CCProfilingEndTimingBlock(CCProfilingTimer* timer) { + struct timeval currentTime; + gettimeofday(¤tTime, NULL); + timersub(¤tTime, &timer->startTime, ¤tTime); + double duration = currentTime.tv_sec * 1000.0 + currentTime.tv_usec / 1000.0; + + // return in milliseconds + timer->averageTime = (timer->averageTime + duration) / 2.0f; +} + +@end + +#endif diff --git a/libs/cocos2d/Support/CGPointExtension.h b/libs/cocos2d/Support/CGPointExtension.h new file mode 100644 index 0000000..5193d3f --- /dev/null +++ b/libs/cocos2d/Support/CGPointExtension.h @@ -0,0 +1,321 @@ +/* cocos2d for iPhone + * http://www.cocos2d-iphone.org + * + * Copyright (c) 2007 Scott Lembcke + * Copyright (c) 2010 Lam Pham + * + * 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. + */ + +/* + * Some of the functions were based on Chipmunk's cpVect.h. + */ + +/** + @file + CGPoint extensions based on Chipmunk's cpVect file. + These extensions work both with CGPoint and cpVect. + + The "ccp" prefix means: "CoCos2d Point" + + Examples: + - ccpAdd( ccp(1,1), ccp(2,2) ); // preferred cocos2d way + - ccpAdd( CGPointMake(1,1), CGPointMake(2,2) ); // also ok but more verbose + + - cpvadd( cpv(1,1), cpv(2,2) ); // way of the chipmunk + - ccpAdd( cpv(1,1), cpv(2,2) ); // mixing chipmunk and cocos2d (avoid) + - cpvadd( CGPointMake(1,1), CGPointMake(2,2) ); // mixing chipmunk and CG (avoid) + */ + +#import + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import +#endif + +#import +#import + +#ifdef __cplusplus +extern "C" { +#endif + +/** Helper macro that creates a CGPoint + @return CGPoint + @since v0.7.2 + */ +#define ccp(__X__,__Y__) CGPointMake(__X__,__Y__) + + +/** Returns opposite of point. + @return CGPoint + @since v0.7.2 + */ +static inline CGPoint +ccpNeg(const CGPoint v) +{ + return ccp(-v.x, -v.y); +} + +/** Calculates sum of two points. + @return CGPoint + @since v0.7.2 + */ +static inline CGPoint +ccpAdd(const CGPoint v1, const CGPoint v2) +{ + return ccp(v1.x + v2.x, v1.y + v2.y); +} + +/** Calculates difference of two points. + @return CGPoint + @since v0.7.2 + */ +static inline CGPoint +ccpSub(const CGPoint v1, const CGPoint v2) +{ + return ccp(v1.x - v2.x, v1.y - v2.y); +} + +/** Returns point multiplied by given factor. + @return CGPoint + @since v0.7.2 + */ +static inline CGPoint +ccpMult(const CGPoint v, const CGFloat s) +{ + return ccp(v.x*s, v.y*s); +} + +/** Calculates midpoint between two points. + @return CGPoint + @since v0.7.2 + */ +static inline CGPoint +ccpMidpoint(const CGPoint v1, const CGPoint v2) +{ + return ccpMult(ccpAdd(v1, v2), 0.5f); +} + +/** Calculates dot product of two points. + @return CGFloat + @since v0.7.2 + */ +static inline CGFloat +ccpDot(const CGPoint v1, const CGPoint v2) +{ + return v1.x*v2.x + v1.y*v2.y; +} + +/** Calculates cross product of two points. + @return CGFloat + @since v0.7.2 + */ +static inline CGFloat +ccpCross(const CGPoint v1, const CGPoint v2) +{ + return v1.x*v2.y - v1.y*v2.x; +} + +/** Calculates perpendicular of v, rotated 90 degrees counter-clockwise -- cross(v, perp(v)) >= 0 + @return CGPoint + @since v0.7.2 + */ +static inline CGPoint +ccpPerp(const CGPoint v) +{ + return ccp(-v.y, v.x); +} + +/** Calculates perpendicular of v, rotated 90 degrees clockwise -- cross(v, rperp(v)) <= 0 + @return CGPoint + @since v0.7.2 + */ +static inline CGPoint +ccpRPerp(const CGPoint v) +{ + return ccp(v.y, -v.x); +} + +/** Calculates the projection of v1 over v2. + @return CGPoint + @since v0.7.2 + */ +static inline CGPoint +ccpProject(const CGPoint v1, const CGPoint v2) +{ + return ccpMult(v2, ccpDot(v1, v2)/ccpDot(v2, v2)); +} + +/** Rotates two points. + @return CGPoint + @since v0.7.2 + */ +static inline CGPoint +ccpRotate(const CGPoint v1, const CGPoint v2) +{ + return ccp(v1.x*v2.x - v1.y*v2.y, v1.x*v2.y + v1.y*v2.x); +} + +/** Unrotates two points. + @return CGPoint + @since v0.7.2 + */ +static inline CGPoint +ccpUnrotate(const CGPoint v1, const CGPoint v2) +{ + return ccp(v1.x*v2.x + v1.y*v2.y, v1.y*v2.x - v1.x*v2.y); +} + +/** Calculates the square length of a CGPoint (not calling sqrt() ) + @return CGFloat + @since v0.7.2 + */ +static inline CGFloat +ccpLengthSQ(const CGPoint v) +{ + return ccpDot(v, v); +} + +/** Calculates distance between point an origin + @return CGFloat + @since v0.7.2 + */ +CGFloat ccpLength(const CGPoint v); + +/** Calculates the distance between two points + @return CGFloat + @since v0.7.2 + */ +CGFloat ccpDistance(const CGPoint v1, const CGPoint v2); + +/** Returns point multiplied to a length of 1. + @return CGPoint + @since v0.7.2 + */ +CGPoint ccpNormalize(const CGPoint v); + +/** Converts radians to a normalized vector. + @return CGPoint + @since v0.7.2 + */ +CGPoint ccpForAngle(const CGFloat a); + +/** Converts a vector to radians. + @return CGFloat + @since v0.7.2 + */ +CGFloat ccpToAngle(const CGPoint v); + + +/** Clamp a value between from and to. + @since v0.99.1 + */ +float clampf(float value, float min_inclusive, float max_inclusive); + +/** Clamp a point between from and to. + @since v0.99.1 + */ +CGPoint ccpClamp(CGPoint p, CGPoint from, CGPoint to); + +/** Quickly convert CGSize to a CGPoint + @since v0.99.1 + */ +CGPoint ccpFromSize(CGSize s); + +/** Run a math operation function on each point component + * absf, fllorf, ceilf, roundf + * any function that has the signature: float func(float); + * For example: let's try to take the floor of x,y + * ccpCompOp(p,floorf); + @since v0.99.1 + */ +CGPoint ccpCompOp(CGPoint p, float (*opFunc)(float)); + +/** Linear Interpolation between two points a and b + @returns + alpha == 0 ? a + alpha == 1 ? b + otherwise a value between a..b + @since v0.99.1 + */ +CGPoint ccpLerp(CGPoint a, CGPoint b, float alpha); + + +/** @returns if points have fuzzy equality which means equal with some degree of variance. + @since v0.99.1 + */ +BOOL ccpFuzzyEqual(CGPoint a, CGPoint b, float variance); + + +/** Multiplies a nd b components, a.x*b.x, a.y*b.y + @returns a component-wise multiplication + @since v0.99.1 + */ +CGPoint ccpCompMult(CGPoint a, CGPoint b); + +/** @returns the signed angle in radians between two vector directions + @since v0.99.1 + */ +float ccpAngleSigned(CGPoint a, CGPoint b); + +/** @returns the angle in radians between two vector directions + @since v0.99.1 +*/ +float ccpAngle(CGPoint a, CGPoint b); + +/** Rotates a point counter clockwise by the angle around a pivot + @param v is the point to rotate + @param pivot is the pivot, naturally + @param angle is the angle of rotation cw in radians + @returns the rotated point + @since v0.99.1 + */ +CGPoint ccpRotateByAngle(CGPoint v, CGPoint pivot, float angle); + +/** A general line-line intersection test + @param p1 + is the startpoint for the first line P1 = (p1 - p2) + @param p2 + is the endpoint for the first line P1 = (p1 - p2) + @param p3 + is the startpoint for the second line P2 = (p3 - p4) + @param p4 + is the endpoint for the second line P2 = (p3 - p4) + @param s + is the range for a hitpoint in P1 (pa = p1 + s*(p2 - p1)) + @param t + is the range for a hitpoint in P3 (pa = p2 + t*(p4 - p3)) + @return bool + indicating successful intersection of a line + note that to truly test intersection for segments we have to make + sure that s & t lie within [0..1] and for rays, make sure s & t > 0 + the hit point is p3 + t * (p4 - p3); + the hit point also is p1 + s * (p2 - p1); + @since v0.99.1 + */ +BOOL ccpLineIntersect(CGPoint p1, CGPoint p2, + CGPoint p3, CGPoint p4, + float *s, float *t); + +#ifdef __cplusplus +} +#endif diff --git a/libs/cocos2d/Support/CGPointExtension.m b/libs/cocos2d/Support/CGPointExtension.m new file mode 100644 index 0000000..2e6be57 --- /dev/null +++ b/libs/cocos2d/Support/CGPointExtension.m @@ -0,0 +1,180 @@ +/* cocos2d for iPhone + * http://www.cocos2d-iphone.org + * + * Copyright (c) 2007 Scott Lembcke + * Copyright (c) 2010 Lam Pham + * + * 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. + */ + +#include "stdio.h" +#include "math.h" + +#import "../ccMacros.h" // CC_SWAP +#include "CGPointExtension.h" + +#define kCGPointEpsilon FLT_EPSILON + +CGFloat +ccpLength(const CGPoint v) +{ + return sqrtf(ccpLengthSQ(v)); +} + +CGFloat +ccpDistance(const CGPoint v1, const CGPoint v2) +{ + return ccpLength(ccpSub(v1, v2)); +} + +CGPoint +ccpNormalize(const CGPoint v) +{ + return ccpMult(v, 1.0f/ccpLength(v)); +} + +CGPoint +ccpForAngle(const CGFloat a) +{ + return ccp(cosf(a), sinf(a)); +} + +CGFloat +ccpToAngle(const CGPoint v) +{ + return atan2f(v.y, v.x); +} + +CGPoint ccpLerp(CGPoint a, CGPoint b, float alpha) +{ + return ccpAdd(ccpMult(a, 1.f - alpha), ccpMult(b, alpha)); +} + +float clampf(float value, float min_inclusive, float max_inclusive) +{ + if (min_inclusive > max_inclusive) { + CC_SWAP(min_inclusive,max_inclusive); + } + return value < min_inclusive ? min_inclusive : value < max_inclusive? value : max_inclusive; +} + +CGPoint ccpClamp(CGPoint p, CGPoint min_inclusive, CGPoint max_inclusive) +{ + return ccp(clampf(p.x,min_inclusive.x,max_inclusive.x), clampf(p.y, min_inclusive.y, max_inclusive.y)); +} + +CGPoint ccpFromSize(CGSize s) +{ + return ccp(s.width, s.height); +} + +CGPoint ccpCompOp(CGPoint p, float (*opFunc)(float)) +{ + return ccp(opFunc(p.x), opFunc(p.y)); +} + +BOOL ccpFuzzyEqual(CGPoint a, CGPoint b, float var) +{ + if(a.x - var <= b.x && b.x <= a.x + var) + if(a.y - var <= b.y && b.y <= a.y + var) + return true; + return false; +} + +CGPoint ccpCompMult(CGPoint a, CGPoint b) +{ + return ccp(a.x * b.x, a.y * b.y); +} + +float ccpAngleSigned(CGPoint a, CGPoint b) +{ + CGPoint a2 = ccpNormalize(a); + CGPoint b2 = ccpNormalize(b); + float angle = atan2f(a2.x * b2.y - a2.y * b2.x, ccpDot(a2, b2)); + if( fabs(angle) < kCGPointEpsilon ) return 0.f; + return angle; +} + +CGPoint ccpRotateByAngle(CGPoint v, CGPoint pivot, float angle) +{ + CGPoint r = ccpSub(v, pivot); + float cosa = cosf(angle), sina = sinf(angle); + float t = r.x; + r.x = t*cosa - r.y*sina + pivot.x; + r.y = t*sina + r.y*cosa + pivot.y; + return r; +} + +BOOL ccpLineIntersect(CGPoint A, CGPoint B, + CGPoint C, CGPoint D, + float *S, float *T) +{ + // FAIL: Line undefined + if ( (A.x==B.x && A.y==B.y) || (C.x==D.x && C.y==D.y) ) return NO; + + // Translate system to make A the origin + B.x-=A.x; B.y-=A.y; + C.x-=A.x; C.y-=A.y; + D.x-=A.x; D.y-=A.y; + + // Cache + CGPoint C2 = C, D2 = D; + + // Length of segment AB + float distAB = sqrtf(B.x*B.x+B.y*B.y); + + // Rotate the system so that point B is on the positive X axis. + float theCos = B.x/distAB; + float theSin = B.y/distAB; + float newX = C.x*theCos+C.y*theSin; + C.y = C.y*theCos-C.x*theSin; C.x = newX; + newX = D.x*theCos+D.y*theSin; + D.y = D.y*theCos-D.x*theSin; D.x = newX; + + // FAIL: Lines are parallel. + if (C.y == D.y) return NO; + + // Discover position of the intersection in the line AB + float ABpos = D.x+(C.x-D.x)*D.y/(D.y-C.y); + + // Vector CD + C.x = D2.x-C2.x; + C.y = D2.y-C2.y; + + // Vector between intersection and point C + A.x = ABpos*theCos-C2.x; + A.y = ABpos*theSin-C2.y; + + newX = sqrtf((A.x*A.x+A.y*A.y)/(C.x*C.x+C.y*C.y)); + if(((A.y<0) != (C.y<0)) || ((A.x<0) != (C.x<0))) + newX *= -1.0f; + + *S = ABpos/distAB; + *T = newX; + + // Success. + return YES; +} + +float ccpAngle(CGPoint a, CGPoint b) +{ + float angle = acosf(ccpDot(ccpNormalize(a), ccpNormalize(b))); + if( fabs(angle) < kCGPointEpsilon ) return 0.f; + return angle; +} diff --git a/libs/cocos2d/Support/OpenGL_Internal.h b/libs/cocos2d/Support/OpenGL_Internal.h new file mode 100644 index 0000000..4789683 --- /dev/null +++ b/libs/cocos2d/Support/OpenGL_Internal.h @@ -0,0 +1,80 @@ +/* + +===== IMPORTANT ===== + +This is sample code demonstrating API, technology or techniques in development. +Although this sample code has been reviewed for technical accuracy, it is not +final. Apple is supplying this information to help you plan for the adoption of +the technologies and programming interfaces described herein. This information +is subject to change, and software implemented based on this sample code should +be tested with final operating system software and final documentation. Newer +versions of this sample code may be provided with future seeds of the API or +technology. For information about updates to this and other developer +documentation, view the New & Updated sidebars in subsequent documentation +seeds. + +===================== + +File: OpenGL_Internal.h +Abstract: This file is included for support purposes and isn't necessary for +understanding this sample. + +Version: 1.0 + +Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. +("Apple") in consideration of your agreement to the following terms, and your +use, installation, modification or redistribution of this Apple software +constitutes acceptance of these terms. If you do not agree with these terms, +please do not use, install, modify or redistribute this Apple software. + +In consideration of your agreement to abide by the following terms, and subject +to these terms, Apple grants you a personal, non-exclusive license, under +Apple's copyrights in this original Apple software (the "Apple Software"), to +use, reproduce, modify and redistribute the Apple Software, with or without +modifications, in source and/or binary forms; provided that if you redistribute +the Apple Software in its entirety and without modifications, you must retain +this notice and the following text and disclaimers in all such redistributions +of the Apple Software. +Neither the name, trademarks, service marks or logos of Apple Inc. may be used +to endorse or promote products derived from the Apple Software without specific +prior written permission from Apple. Except as expressly stated in this notice, +no other rights or licenses, express or implied, are granted by Apple herein, +including but not limited to any patent rights that may be infringed by your +derivative works or by other works in which the Apple Software may be +incorporated. + +The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO +WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED +WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN +COMBINATION WITH YOUR PRODUCTS. + +IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR +DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF +CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Copyright (C) 2008 Apple Inc. All Rights Reserved. + +*/ + +/* Generic error reporting */ +#define REPORT_ERROR(__FORMAT__, ...) printf("%s: %s\n", __FUNCTION__, [[NSString stringWithFormat:__FORMAT__, __VA_ARGS__] UTF8String]) + +/* EAGL and GL functions calling wrappers that log on error */ +#define CALL_EAGL_FUNCTION(__FUNC__, ...) ({ EAGLError __error = __FUNC__( __VA_ARGS__ ); if(__error != kEAGLErrorSuccess) printf("%s() called from %s returned error %i\n", #__FUNC__, __FUNCTION__, __error); (__error ? NO : YES); }) +//#define CHECK_GL_ERROR() ({ GLenum __error = glGetError(); if(__error) printf("OpenGL error 0x%04X in %s\n", __error, __FUNCTION__); (__error ? NO : YES); }) +#define CHECK_GL_ERROR() ({ GLenum __error = glGetError(); if(__error) printf("OpenGL error 0x%04X in %s\n", __error, __FUNCTION__); }) + +/* Optional delegate methods support */ +#ifndef __DELEGATE_IVAR__ +#define __DELEGATE_IVAR__ _delegate +#endif +#ifndef __DELEGATE_METHODS_IVAR__ +#define __DELEGATE_METHODS_IVAR__ _delegateMethods +#endif +#define TEST_DELEGATE_METHOD_BIT(__BIT__) (self->__DELEGATE_METHODS_IVAR__ & (1 << __BIT__)) +#define SET_DELEGATE_METHOD_BIT(__BIT__, __NAME__) { if([self->__DELEGATE_IVAR__ respondsToSelector:@selector(__NAME__)]) self->__DELEGATE_METHODS_IVAR__ |= (1 << __BIT__); else self->__DELEGATE_METHODS_IVAR__ &= ~(1 << __BIT__); } diff --git a/libs/cocos2d/Support/TGAlib.h b/libs/cocos2d/Support/TGAlib.h new file mode 100644 index 0000000..247084e --- /dev/null +++ b/libs/cocos2d/Support/TGAlib.h @@ -0,0 +1,55 @@ +// +// TGA lib for cocos2d-iphone +// +// sources from: http://www.lighthouse3d.com/opengl/terrain/index.php3?tgasource +// + +//#ifndef TGA_LIB +//#define TGA_LIB + +/** + @file + TGA image support + */ + +enum { + TGA_OK, + TGA_ERROR_FILE_OPEN, + TGA_ERROR_READING_FILE, + TGA_ERROR_INDEXED_COLOR, + TGA_ERROR_MEMORY, + TGA_ERROR_COMPRESSED_FILE, +}; + +/** TGA format */ +typedef struct sImageTGA { + int status; + unsigned char type, pixelDepth; + + /** map width */ + short int width; + + /** map height */ + short int height; + + /** raw data */ + unsigned char *imageData; + int flipped; +} tImageTGA; + +/// load the image header fields. We only keep those that matter! +void tgaLoadHeader(FILE *file, tImageTGA *info); + +/// loads the image pixels. You shouldn't call this function directly +void tgaLoadImageData(FILE *file, tImageTGA *info); + +/// this is the function to call when we want to load an image +tImageTGA * tgaLoad(const char *filename); + +// /converts RGB to greyscale +void tgaRGBtogreyscale(tImageTGA *info); + +/// releases the memory used for the image +void tgaDestroy(tImageTGA *info); + +//#endif // TGA_LIB diff --git a/libs/cocos2d/Support/TGAlib.m b/libs/cocos2d/Support/TGAlib.m new file mode 100644 index 0000000..b574d59 --- /dev/null +++ b/libs/cocos2d/Support/TGAlib.m @@ -0,0 +1,272 @@ +// +// TGA lib for cocos2d-iphone +// +// sources from: http://www.lighthouse3d.com/opengl/terrain/index.php3?tgasource +// +// TGA RLE compression support by Ernesto Corvi + +#include +#include +#include + +#import "TGAlib.h" + + +// load the image header fields. We only keep those that matter! +void tgaLoadHeader(FILE *file, tImageTGA *info) { + unsigned char cGarbage; + short int iGarbage; + + fread(&cGarbage, sizeof(unsigned char), 1, file); + fread(&cGarbage, sizeof(unsigned char), 1, file); + + // type must be 2 or 3 + fread(&info->type, sizeof(unsigned char), 1, file); + + fread(&iGarbage, sizeof(short int), 1, file); + fread(&iGarbage, sizeof(short int), 1, file); + fread(&cGarbage, sizeof(unsigned char), 1, file); + fread(&iGarbage, sizeof(short int), 1, file); + fread(&iGarbage, sizeof(short int), 1, file); + + fread(&info->width, sizeof(short int), 1, file); + fread(&info->height, sizeof(short int), 1, file); + fread(&info->pixelDepth, sizeof(unsigned char), 1, file); + + fread(&cGarbage, sizeof(unsigned char), 1, file); + + info->flipped = 0; + if ( cGarbage & 0x20 ) info->flipped = 1; +} + +// loads the image pixels. You shouldn't call this function directly +void tgaLoadImageData(FILE *file, tImageTGA *info) { + + int mode,total,i; + unsigned char aux; + + // mode equal the number of components for each pixel + mode = info->pixelDepth / 8; + // total is the number of unsigned chars we'll have to read + total = info->height * info->width * mode; + + fread(info->imageData,sizeof(unsigned char),total,file); + + // mode=3 or 4 implies that the image is RGB(A). However TGA + // stores it as BGR(A) so we'll have to swap R and B. + if (mode >= 3) + for (i=0; i < total; i+= mode) { + aux = info->imageData[i]; + info->imageData[i] = info->imageData[i+2]; + info->imageData[i+2] = aux; + } +} + +// loads the RLE encoded image pixels. You shouldn't call this function directly +void tgaLoadRLEImageData(FILE *file, tImageTGA *info) +{ + unsigned int mode,total,i, index = 0; + unsigned char aux[4], runlength = 0; + unsigned int skip = 0, flag = 0; + + // mode equal the number of components for each pixel + mode = info->pixelDepth / 8; + // total is the number of unsigned chars we'll have to read + total = info->height * info->width; + + for( i = 0; i < total; i++ ) + { + // if we have a run length pending, run it + if ( runlength != 0 ) + { + // we do, update the run length count + runlength--; + skip = (flag != 0); + } + else + { + // otherwise, read in the run length token + if ( fread(&runlength,sizeof(unsigned char),1,file) != 1 ) + return; + + // see if it's a RLE encoded sequence + flag = runlength & 0x80; + if ( flag ) runlength -= 128; + skip = 0; + } + + // do we need to skip reading this pixel? + if ( !skip ) + { + // no, read in the pixel data + if ( fread(aux,sizeof(unsigned char),mode,file) != mode ) + return; + + // mode=3 or 4 implies that the image is RGB(A). However TGA + // stores it as BGR(A) so we'll have to swap R and B. + if ( mode >= 3 ) + { + unsigned char tmp; + + tmp = aux[0]; + aux[0] = aux[2]; + aux[2] = tmp; + } + } + + // add the pixel to our image + memcpy(&info->imageData[index], aux, mode); + index += mode; + } +} + +void tgaFlipImage( tImageTGA *info ) +{ + // mode equal the number of components for each pixel + int mode = info->pixelDepth / 8; + int rowbytes = info->width*mode; + unsigned char *row = (unsigned char *)malloc(rowbytes); + int y; + + if (row == NULL) return; + + for( y = 0; y < (info->height/2); y++ ) + { + memcpy(row, &info->imageData[y*rowbytes],rowbytes); + memcpy(&info->imageData[y*rowbytes], &info->imageData[(info->height-(y+1))*rowbytes], rowbytes); + memcpy(&info->imageData[(info->height-(y+1))*rowbytes], row, rowbytes); + } + + free(row); + info->flipped = 0; +} + +// this is the function to call when we want to load an image +tImageTGA * tgaLoad(const char *filename) { + + FILE *file; + tImageTGA *info; + int mode,total; + + // allocate memory for the info struct and check! + info = (tImageTGA *)malloc(sizeof(tImageTGA)); + if (info == NULL) + return(NULL); + + + // open the file for reading (binary mode) + file = fopen(filename, "rb"); + if (file == NULL) { + info->status = TGA_ERROR_FILE_OPEN; + return(info); + } + + // load the header + tgaLoadHeader(file,info); + + // check for errors when loading the header + if (ferror(file)) { + info->status = TGA_ERROR_READING_FILE; + fclose(file); + return(info); + } + + // check if the image is color indexed + if (info->type == 1) { + info->status = TGA_ERROR_INDEXED_COLOR; + fclose(file); + return(info); + } + // check for other types (compressed images) + if ((info->type != 2) && (info->type !=3) && (info->type !=10) ) { + info->status = TGA_ERROR_COMPRESSED_FILE; + fclose(file); + return(info); + } + + // mode equals the number of image components + mode = info->pixelDepth / 8; + // total is the number of unsigned chars to read + total = info->height * info->width * mode; + // allocate memory for image pixels + info->imageData = (unsigned char *)malloc(sizeof(unsigned char) * + total); + + // check to make sure we have the memory required + if (info->imageData == NULL) { + info->status = TGA_ERROR_MEMORY; + fclose(file); + return(info); + } + // finally load the image pixels + if ( info->type == 10 ) + tgaLoadRLEImageData(file, info); + else + tgaLoadImageData(file,info); + + // check for errors when reading the pixels + if (ferror(file)) { + info->status = TGA_ERROR_READING_FILE; + fclose(file); + return(info); + } + fclose(file); + info->status = TGA_OK; + + if ( info->flipped ) + { + tgaFlipImage( info ); + if ( info->flipped ) info->status = TGA_ERROR_MEMORY; + } + + return(info); +} + +// converts RGB to greyscale +void tgaRGBtogreyscale(tImageTGA *info) { + + int mode,i,j; + + unsigned char *newImageData; + + // if the image is already greyscale do nothing + if (info->pixelDepth == 8) + return; + + // compute the number of actual components + mode = info->pixelDepth / 8; + + // allocate an array for the new image data + newImageData = (unsigned char *)malloc(sizeof(unsigned char) * + info->height * info->width); + if (newImageData == NULL) { + return; + } + + // convert pixels: greyscale = o.30 * R + 0.59 * G + 0.11 * B + for (i = 0,j = 0; j < info->width * info->height; i +=mode, j++) + newImageData[j] = + (unsigned char)(0.30 * info->imageData[i] + + 0.59 * info->imageData[i+1] + + 0.11 * info->imageData[i+2]); + + + //free old image data + free(info->imageData); + + // reassign pixelDepth and type according to the new image type + info->pixelDepth = 8; + info->type = 3; + // reassing imageData to the new array. + info->imageData = newImageData; +} + +// releases the memory used for the image +void tgaDestroy(tImageTGA *info) { + + if (info != NULL) { + if (info->imageData != NULL) + free(info->imageData); + free(info); + } +} diff --git a/libs/cocos2d/Support/TransformUtils.h b/libs/cocos2d/Support/TransformUtils.h new file mode 100644 index 0000000..49fde35 --- /dev/null +++ b/libs/cocos2d/Support/TransformUtils.h @@ -0,0 +1,37 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Valentin Milea + * + * 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 + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import +#import +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import +#import +#endif + +void CGAffineToGL(const CGAffineTransform *t, GLfloat *m); +void GLToCGAffine(const GLfloat *m, CGAffineTransform *t); diff --git a/libs/cocos2d/Support/TransformUtils.m b/libs/cocos2d/Support/TransformUtils.m new file mode 100644 index 0000000..9caecf0 --- /dev/null +++ b/libs/cocos2d/Support/TransformUtils.m @@ -0,0 +1,46 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Valentin Milea + * + * 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 "TransformUtils.h" + +void CGAffineToGL(const CGAffineTransform *t, GLfloat *m) +{ + // | m[0] m[4] m[8] m[12] | | m11 m21 m31 m41 | | a c 0 tx | + // | m[1] m[5] m[9] m[13] | | m12 m22 m32 m42 | | b d 0 ty | + // | m[2] m[6] m[10] m[14] | <=> | m13 m23 m33 m43 | <=> | 0 0 1 0 | + // | m[3] m[7] m[11] m[15] | | m14 m24 m34 m44 | | 0 0 0 1 | + + m[2] = m[3] = m[6] = m[7] = m[8] = m[9] = m[11] = m[14] = 0.0f; + m[10] = m[15] = 1.0f; + m[0] = t->a; m[4] = t->c; m[12] = t->tx; + m[1] = t->b; m[5] = t->d; m[13] = t->ty; +} + +void GLToCGAffine(const GLfloat *m, CGAffineTransform *t) +{ + t->a = m[0]; t->c = m[4]; t->tx = m[12]; + t->b = m[1]; t->d = m[5]; t->ty = m[13]; +} diff --git a/libs/cocos2d/Support/ZipUtils.h b/libs/cocos2d/Support/ZipUtils.h new file mode 100644 index 0000000..8179e4c --- /dev/null +++ b/libs/cocos2d/Support/ZipUtils.h @@ -0,0 +1,78 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * + * inflateMemory_ based on zlib example code + * http://www.zlib.net + * + * Some ideas were taken from: + * http://themanaworld.org/ + * from the mapreader.cpp file + * + */ + +#ifndef __CC_ZIP_UTILS_H +#define __CC_ZIP_UTILS_H + +#import + +#ifdef __cplusplus +extern "C" { +#endif + + /* XXX: pragma pack ??? */ + /** @struct CCZHeader + */ + struct CCZHeader { + uint8_t sig[4]; // signature. Should be 'CCZ!' 4 bytes + uint16_t compression_type; // should 0 + uint16_t version; // should be 2 (although version type==1 is also supported) + uint32_t reserved; // Reserverd for users. + uint32_t len; // size of the uncompressed file + }; + + enum { + CCZ_COMPRESSION_ZLIB, // zlib format. + CCZ_COMPRESSION_BZIP2, // bzip2 format (not supported yet) + CCZ_COMPRESSION_GZIP, // gzip format (not supported yet) + CCZ_COMPRESSION_NONE, // plain (not supported yet) + }; + +/** @file + * Zip helper functions + */ + +/** + * Inflates either zlib or gzip deflated memory. The inflated memory is + * expected to be freed by the caller. + * + * @returns the length of the deflated buffer + * + @since v0.8.1 + */ +int ccInflateMemory(unsigned char *in, unsigned int inLength, unsigned char **out); + + +/** inflates a GZip file into memory + * + * @returns the length of the deflated buffer + * + * @since v0.99.5 + */ +int ccInflateGZipFile(const char *filename, unsigned char **out); + +/** inflates a CCZ file into memory + * + * @returns the length of the deflated buffer + * + * @since v0.99.5 + */ +int ccInflateCCZFile(const char *filename, unsigned char **out); + + +#ifdef __cplusplus +} +#endif + +#endif // __CC_ZIP_UTILS_H diff --git a/libs/cocos2d/Support/ZipUtils.m b/libs/cocos2d/Support/ZipUtils.m new file mode 100644 index 0000000..6d59a1b --- /dev/null +++ b/libs/cocos2d/Support/ZipUtils.m @@ -0,0 +1,246 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * + * Inflates either zlib or gzip deflated memory. The inflated memory is + * expected to be freed by the caller. + * + * inflateMemory_ based on zlib example code + * http://www.zlib.net + * + * Some ideas were taken from: + * http://themanaworld.org/ + * from the mapreader.cpp file + */ + +#import + +#import +#import +#import +#import + +#import "ZipUtils.h" +#import "CCFileUtils.h" +#import "../ccMacros.h" + +// memory in iPhone is precious +// Should buffer factor be 1.5 instead of 2 ? +#define BUFFER_INC_FACTOR (2) + +static int inflateMemory_(unsigned char *in, unsigned int inLength, unsigned char **out, unsigned int *outLength) +{ + /* ret value */ + int err = Z_OK; + + /* 256k initial decompress buffer */ + int bufferSize = 256 * 1024; + *out = (unsigned char*) malloc(bufferSize); + + z_stream d_stream; /* decompression stream */ + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = in; + d_stream.avail_in = inLength; + d_stream.next_out = *out; + d_stream.avail_out = bufferSize; + + /* window size to hold 256k */ + if( (err = inflateInit2(&d_stream, 15 + 32)) != Z_OK ) + return err; + + for (;;) { + err = inflate(&d_stream, Z_NO_FLUSH); + + if (err == Z_STREAM_END) + break; + + switch (err) { + case Z_NEED_DICT: + err = Z_DATA_ERROR; + case Z_DATA_ERROR: + case Z_MEM_ERROR: + inflateEnd(&d_stream); + return err; + } + + // not enough memory ? + if (err != Z_STREAM_END) { + + unsigned char *tmp = realloc(*out, bufferSize * BUFFER_INC_FACTOR); + + /* not enough memory, ouch */ + if (! tmp ) { + CCLOG(@"cocos2d: ZipUtils: realloc failed"); + inflateEnd(&d_stream); + return Z_MEM_ERROR; + } + /* only assign to *out if tmp is valid. it's not guaranteed that realloc will reuse the memory */ + *out = tmp; + + d_stream.next_out = *out + bufferSize; + d_stream.avail_out = bufferSize; + bufferSize *= BUFFER_INC_FACTOR; + } + } + + + *outLength = bufferSize - d_stream.avail_out; + err = inflateEnd(&d_stream); + return err; +} + +int ccInflateMemory(unsigned char *in, unsigned int inLength, unsigned char **out) +{ + unsigned int outLength = 0; + int err = inflateMemory_(in, inLength, out, &outLength); + + if (err != Z_OK || *out == NULL) { + if (err == Z_MEM_ERROR) + CCLOG(@"cocos2d: ZipUtils: Out of memory while decompressing map data!"); + + else if (err == Z_VERSION_ERROR) + CCLOG(@"cocos2d: ZipUtils: Incompatible zlib version!"); + + else if (err == Z_DATA_ERROR) + CCLOG(@"cocos2d: ZipUtils: Incorrect zlib compressed data!"); + + else + CCLOG(@"cocos2d: ZipUtils: Unknown error while decompressing map data!"); + + free(*out); + *out = NULL; + outLength = 0; + } + + return outLength; +} + +int ccInflateGZipFile(const char *path, unsigned char **out) +{ + int len; + unsigned int offset = 0; + + assert( out ); + assert( &*out ); + + gzFile inFile = gzopen(path, "rb"); + if( inFile == NULL ) { + CCLOG(@"cocos2d: ZipUtils: error open gzip file: %s", path); + return -1; + } + + /* 512k initial decompress buffer */ + unsigned int bufferSize = 512 * 1024; + unsigned int totalBufferSize = bufferSize; + + *out = malloc( bufferSize ); + if( ! out ) { + CCLOG(@"cocos2d: ZipUtils: out of memory"); + return -1; + } + + for (;;) { + len = gzread(inFile, *out + offset, bufferSize); + if (len < 0) { + CCLOG(@"cocos2d: ZipUtils: error in gzread"); + free( *out ); + *out = NULL; + return -1; + } + if (len == 0) + break; + + offset += len; + + // finish reading the file + if( len < bufferSize ) + break; + + bufferSize *= BUFFER_INC_FACTOR; + totalBufferSize += bufferSize; + unsigned char *tmp = realloc(*out, totalBufferSize ); + + if( ! tmp ) { + CCLOG(@"cocos2d: ZipUtils: out of memory"); + free( *out ); + *out = NULL; + return -1; + } + + *out = tmp; + } + + if (gzclose(inFile) != Z_OK) + CCLOG(@"cocos2d: ZipUtils: gzclose failed"); + + return offset; +} + +int ccInflateCCZFile(const char *path, unsigned char **out) +{ + assert( out ); + assert( &*out ); + + // load file into memory + unsigned char *compressed = NULL; + NSInteger fileLen = ccLoadFileIntoMemory( path, &compressed ); + if( fileLen < 0 ) { + CCLOG(@"cocos2d: Error loading CCZ compressed file"); + } + + struct CCZHeader *header = (struct CCZHeader*) compressed; + + // verify header + if( header->sig[0] != 'C' || header->sig[1] != 'C' || header->sig[2] != 'Z' || header->sig[3] != '!' ) { + CCLOG(@"cocos2d: Invalid CCZ file"); + free(compressed); + return -1; + } + + // verify header version + uint16_t version = CFSwapInt16BigToHost( header->version ); + if( version > 2 ) { + CCLOG(@"cocos2d: Unsupported CCZ header format"); + free(compressed); + return -1; + } + + // verify compression format + if( CFSwapInt16BigToHost(header->compression_type) != CCZ_COMPRESSION_ZLIB ) { + CCLOG(@"cocos2d: CCZ Unsupported compression method"); + free(compressed); + return -1; + } + + uint32_t len = CFSwapInt32BigToHost( header->len ); + + *out = malloc( len ); + if(! *out ) + { + CCLOG(@"cocos2d: CCZ: Failed to allocate memory for texture"); + free(compressed); + return -1; + } + + + uLongf destlen = len; + uLongf source = (uLongf) compressed + sizeof(*header); + int ret = uncompress(*out, &destlen, (Bytef*)source, fileLen - sizeof(*header) ); + + free( compressed ); + + if( ret != Z_OK ) + { + CCLOG(@"cocos2d: CCZ: Failed to uncompress data"); + free( *out ); + *out = NULL; + return -1; + } + + + return len; +} \ No newline at end of file diff --git a/libs/cocos2d/Support/base64.c b/libs/cocos2d/Support/base64.c new file mode 100644 index 0000000..7a8f65a --- /dev/null +++ b/libs/cocos2d/Support/base64.c @@ -0,0 +1,89 @@ +/* + public domain BASE64 code + + modified for cocos2d-iphone: http://www.cocos2d-iphone.org + */ + +#include +#include + +unsigned char alphabet[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +int _base64Decode( unsigned char *input, unsigned int input_len, unsigned char *output, unsigned int *output_len ) +{ + static char inalphabet[256], decoder[256]; + int i, bits, c, char_count, errors = 0; + unsigned int input_idx = 0; + unsigned int output_idx = 0; + + for (i = (sizeof alphabet) - 1; i >= 0 ; i--) { + inalphabet[alphabet[i]] = 1; + decoder[alphabet[i]] = i; + } + + char_count = 0; + bits = 0; + for( input_idx=0; input_idx < input_len ; input_idx++ ) { + c = input[ input_idx ]; + if (c == '=') + break; + if (c > 255 || ! inalphabet[c]) + continue; + bits += decoder[c]; + char_count++; + if (char_count == 4) { + output[ output_idx++ ] = (bits >> 16); + output[ output_idx++ ] = ((bits >> 8) & 0xff); + output[ output_idx++ ] = ( bits & 0xff); + bits = 0; + char_count = 0; + } else { + bits <<= 6; + } + } + + if( c == '=' ) { + switch (char_count) { + case 1: + fprintf(stderr, "base64Decode: encoding incomplete: at least 2 bits missing"); + errors++; + break; + case 2: + output[ output_idx++ ] = ( bits >> 10 ); + break; + case 3: + output[ output_idx++ ] = ( bits >> 16 ); + output[ output_idx++ ] = (( bits >> 8 ) & 0xff); + break; + } + } else if ( input_idx < input_len ) { + if (char_count) { + fprintf(stderr, "base64 encoding incomplete: at least %d bits truncated", + ((4 - char_count) * 6)); + errors++; + } + } + + *output_len = output_idx; + return errors; +} + +int base64Decode(unsigned char *in, unsigned int inLength, unsigned char **out) +{ + unsigned int outLength = 0; + + //should be enough to store 6-bit buffers in 8-bit buffers + *out = malloc( inLength * 3.0f / 4.0f + 1 ); + if( *out ) { + int ret = _base64Decode(in, inLength, *out, &outLength); + + if (ret > 0 ) + { + printf("Base64Utils: error decoding"); + free(*out); + *out = NULL; + outLength = 0; + } + } + return outLength; +} diff --git a/libs/cocos2d/Support/base64.h b/libs/cocos2d/Support/base64.h new file mode 100644 index 0000000..d30878e --- /dev/null +++ b/libs/cocos2d/Support/base64.h @@ -0,0 +1,33 @@ +/* + public domain BASE64 code + + modified for cocos2d-iphone: http://www.cocos2d-iphone.org + */ + +#ifndef __CC_BASE64_DECODE_H +#define __CC_BASE64_DECODE_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/** @file + base64 helper functions + */ + +/** + * Decodes a 64base encoded memory. The decoded memory is + * expected to be freed by the caller. + * + * @returns the length of the out buffer + * + @since v0.8.1 + */ +int base64Decode(unsigned char *in, unsigned int inLength, unsigned char **out); + +#ifdef __cplusplus +} +#endif + +#endif // __CC_BASE64_DECODE_H diff --git a/libs/cocos2d/Support/ccCArray.h b/libs/cocos2d/Support/ccCArray.h new file mode 100644 index 0000000..c0a91b1 --- /dev/null +++ b/libs/cocos2d/Support/ccCArray.h @@ -0,0 +1,420 @@ +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +/** + @file + Based on Chipmunk cpArray. + ccArray is a faster alternative to NSMutableArray, it does pretty much the + same thing (stores NSObjects and retains/releases them appropriately). It's + faster because: + - it uses a plain C interface so it doesn't incur Objective-c messaging overhead + - it assumes you know what you're doing, so it doesn't spend time on safety checks + (index out of bounds, required capacity etc.) + - comparisons are done using pointer equality instead of isEqual + + There are 2 kind of functions: + - ccArray functions that manipulates objective-c objects (retain and release are performanced) + - ccCArray functions that manipulates values like if they were standard C structures (no retain/release is performed) + */ + +#ifndef CC_ARRAY_H +#define CC_ARRAY_H + +#import + +#import +#import + + +#pragma mark - +#pragma mark ccArray for Objects + +// Easy integration +#define CCARRAYDATA_FOREACH(__array__, __object__) \ +__object__=__array__->arr[0]; for(NSUInteger i=0, num=__array__->num; iarr[i]) \ + + +typedef struct ccArray { + NSUInteger num, max; + id *arr; +} ccArray; + +/** Allocates and initializes a new array with specified capacity */ +static inline ccArray* ccArrayNew(NSUInteger capacity) { + if (capacity == 0) + capacity = 1; + + ccArray *arr = (ccArray*)malloc( sizeof(ccArray) ); + arr->num = 0; + arr->arr = (id*) malloc( capacity * sizeof(id) ); + arr->max = capacity; + + return arr; +} + +static inline void ccArrayRemoveAllObjects(ccArray *arr); + +/** Frees array after removing all remaining objects. Silently ignores nil arr. */ +static inline void ccArrayFree(ccArray *arr) +{ + if( arr == nil ) return; + + ccArrayRemoveAllObjects(arr); + + free(arr->arr); + free(arr); +} + +/** Doubles array capacity */ +static inline void ccArrayDoubleCapacity(ccArray *arr) +{ + arr->max *= 2; + arr->arr = (id*) realloc( arr->arr, arr->max * sizeof(id) ); +} + +/** Increases array capacity such that max >= num + extra. */ +static inline void ccArrayEnsureExtraCapacity(ccArray *arr, NSUInteger extra) +{ + while (arr->max < arr->num + extra) + ccArrayDoubleCapacity(arr); +} + +/** Returns index of first occurence of object, NSNotFound if object not found. */ +static inline NSUInteger ccArrayGetIndexOfObject(ccArray *arr, id object) +{ + for( NSUInteger i = 0; i < arr->num; i++) + if( arr->arr[i] == object ) return i; + + return NSNotFound; +} + +/** Returns a Boolean value that indicates whether object is present in array. */ +static inline BOOL ccArrayContainsObject(ccArray *arr, id object) +{ + return ccArrayGetIndexOfObject(arr, object) != NSNotFound; +} + +/** Appends an object. Bahaviour undefined if array doesn't have enough capacity. */ +static inline void ccArrayAppendObject(ccArray *arr, id object) +{ + arr->arr[arr->num] = [object retain]; + arr->num++; +} + +/** Appends an object. Capacity of arr is increased if needed. */ +static inline void ccArrayAppendObjectWithResize(ccArray *arr, id object) +{ + ccArrayEnsureExtraCapacity(arr, 1); + ccArrayAppendObject(arr, object); +} + +/** Appends objects from plusArr to arr. Behaviour undefined if arr doesn't have + enough capacity. */ +static inline void ccArrayAppendArray(ccArray *arr, ccArray *plusArr) +{ + for( NSUInteger i = 0; i < plusArr->num; i++) + ccArrayAppendObject(arr, plusArr->arr[i]); +} + +/** Appends objects from plusArr to arr. Capacity of arr is increased if needed. */ +static inline void ccArrayAppendArrayWithResize(ccArray *arr, ccArray *plusArr) +{ + ccArrayEnsureExtraCapacity(arr, plusArr->num); + ccArrayAppendArray(arr, plusArr); +} + +/** Inserts an object at index */ +static inline void ccArrayInsertObjectAtIndex(ccArray *arr, id object, NSUInteger index) +{ + NSCAssert(index<=arr->num, @"Invalid index. Out of bounds"); + + ccArrayEnsureExtraCapacity(arr, 1); + + NSUInteger remaining = arr->num - index; + if( remaining > 0) + memmove(&arr->arr[index+1], &arr->arr[index], sizeof(id) * remaining ); + + arr->arr[index] = [object retain]; + arr->num++; +} + +/** Swaps two objects */ +static inline void ccArraySwapObjectsAtIndexes(ccArray *arr, NSUInteger index1, NSUInteger index2) +{ + NSCAssert(index1 < arr->num, @"(1) Invalid index. Out of bounds"); + NSCAssert(index2 < arr->num, @"(2) Invalid index. Out of bounds"); + + id object1 = arr->arr[index1]; + + arr->arr[index1] = arr->arr[index2]; + arr->arr[index2] = object1; +} + +/** Removes all objects from arr */ +static inline void ccArrayRemoveAllObjects(ccArray *arr) +{ + while( arr->num > 0 ) + [arr->arr[--arr->num] release]; +} + +/** Removes object at specified index and pushes back all subsequent objects. + Behaviour undefined if index outside [0, num-1]. */ +static inline void ccArrayRemoveObjectAtIndex(ccArray *arr, NSUInteger index) +{ + [arr->arr[index] release]; + arr->num--; + + NSUInteger remaining = arr->num - index; + if(remaining>0) + memmove(&arr->arr[index], &arr->arr[index+1], remaining * sizeof(id)); +} + +/** Removes object at specified index and fills the gap with the last object, + thereby avoiding the need to push back subsequent objects. + Behaviour undefined if index outside [0, num-1]. */ +static inline void ccArrayFastRemoveObjectAtIndex(ccArray *arr, NSUInteger index) +{ + [arr->arr[index] release]; + NSUInteger last = --arr->num; + arr->arr[index] = arr->arr[last]; +} + +static inline void ccArrayFastRemoveObject(ccArray *arr, id object) +{ + NSUInteger index = ccArrayGetIndexOfObject(arr, object); + if (index != NSNotFound) + ccArrayFastRemoveObjectAtIndex(arr, index); +} + +/** Searches for the first occurance of object and removes it. If object is not + found the function has no effect. */ +static inline void ccArrayRemoveObject(ccArray *arr, id object) +{ + NSUInteger index = ccArrayGetIndexOfObject(arr, object); + if (index != NSNotFound) + ccArrayRemoveObjectAtIndex(arr, index); +} + +/** Removes from arr all objects in minusArr. For each object in minusArr, the + first matching instance in arr will be removed. */ +static inline void ccArrayRemoveArray(ccArray *arr, ccArray *minusArr) +{ + for( NSUInteger i = 0; i < minusArr->num; i++) + ccArrayRemoveObject(arr, minusArr->arr[i]); +} + +/** Removes from arr all objects in minusArr. For each object in minusArr, all + matching instances in arr will be removed. */ +static inline void ccArrayFullRemoveArray(ccArray *arr, ccArray *minusArr) +{ + NSUInteger back = 0; + + for( NSUInteger i = 0; i < arr->num; i++) { + if( ccArrayContainsObject(minusArr, arr->arr[i]) ) { + [arr->arr[i] release]; + back++; + } else + arr->arr[i - back] = arr->arr[i]; + } + + arr->num -= back; +} + +/** Sends to each object in arr the message identified by given selector. */ +static inline void ccArrayMakeObjectsPerformSelector(ccArray *arr, SEL sel) +{ + for( NSUInteger i = 0; i < arr->num; i++) + [arr->arr[i] performSelector:sel]; +} + +static inline void ccArrayMakeObjectsPerformSelectorWithObject(ccArray *arr, SEL sel, id object) +{ + for( NSUInteger i = 0; i < arr->num; i++) + [arr->arr[i] performSelector:sel withObject:object]; +} + + +#pragma mark - +#pragma mark ccCArray for Values (c structures) + +typedef ccArray ccCArray; + +static inline void ccCArrayRemoveAllValues(ccCArray *arr); + +/** Allocates and initializes a new C array with specified capacity */ +static inline ccCArray* ccCArrayNew(NSUInteger capacity) { + if (capacity == 0) + capacity = 1; + + ccCArray *arr = (ccCArray*)malloc( sizeof(ccCArray) ); + arr->num = 0; + arr->arr = (id*) malloc( capacity * sizeof(id) ); + arr->max = capacity; + + return arr; +} + +/** Frees C array after removing all remaining values. Silently ignores nil arr. */ +static inline void ccCArrayFree(ccCArray *arr) +{ + if( arr == nil ) return; + + ccCArrayRemoveAllValues(arr); + + free(arr->arr); + free(arr); +} + +/** Doubles C array capacity */ +static inline void ccCArrayDoubleCapacity(ccCArray *arr) +{ + return ccArrayDoubleCapacity(arr); +} + +/** Increases array capacity such that max >= num + extra. */ +static inline void ccCArrayEnsureExtraCapacity(ccCArray *arr, NSUInteger extra) +{ + return ccArrayEnsureExtraCapacity(arr,extra); +} + +/** Returns index of first occurence of value, NSNotFound if value not found. */ +static inline NSUInteger ccCArrayGetIndexOfValue(ccCArray *arr, void* value) +{ + for( NSUInteger i = 0; i < arr->num; i++) + if( arr->arr[i] == value ) return i; + return NSNotFound; +} + +/** Returns a Boolean value that indicates whether value is present in the C array. */ +static inline BOOL ccCArrayContainsValue(ccCArray *arr, void* value) +{ + return ccCArrayGetIndexOfValue(arr, value) != NSNotFound; +} + +/** Inserts a value at a certain position. Behaviour undefined if aray doesn't have enough capacity */ +static inline void ccCArrayInsertValueAtIndex( ccCArray *arr, void *value, NSUInteger index) +{ + assert( index < arr->max ); + + NSUInteger remaining = arr->num - index; + + // last Value doesn't need to be moved + if( remaining > 0) { + // tex coordinates + memmove( &arr->arr[index+1],&arr->arr[index], sizeof(void*) * remaining ); + } + + arr->num++; + arr->arr[index] = (id) value; +} + +/** Appends an value. Bahaviour undefined if array doesn't have enough capacity. */ +static inline void ccCArrayAppendValue(ccCArray *arr, void* value) +{ + arr->arr[arr->num] = (id) value; + arr->num++; +} + +/** Appends an value. Capacity of arr is increased if needed. */ +static inline void ccCArrayAppendValueWithResize(ccCArray *arr, void* value) +{ + ccCArrayEnsureExtraCapacity(arr, 1); + ccCArrayAppendValue(arr, value); +} + +/** Appends values from plusArr to arr. Behaviour undefined if arr doesn't have + enough capacity. */ +static inline void ccCArrayAppendArray(ccCArray *arr, ccCArray *plusArr) +{ + for( NSUInteger i = 0; i < plusArr->num; i++) + ccCArrayAppendValue(arr, plusArr->arr[i]); +} + +/** Appends values from plusArr to arr. Capacity of arr is increased if needed. */ +static inline void ccCArrayAppendArrayWithResize(ccCArray *arr, ccCArray *plusArr) +{ + ccCArrayEnsureExtraCapacity(arr, plusArr->num); + ccCArrayAppendArray(arr, plusArr); +} + +/** Removes all values from arr */ +static inline void ccCArrayRemoveAllValues(ccCArray *arr) +{ + arr->num = 0; +} + +/** Removes value at specified index and pushes back all subsequent values. + Behaviour undefined if index outside [0, num-1]. + @since v0.99.4 + */ +static inline void ccCArrayRemoveValueAtIndex(ccCArray *arr, NSUInteger index) +{ + for( NSUInteger last = --arr->num; index < last; index++) + arr->arr[index] = arr->arr[index + 1]; +} + +/** Removes value at specified index and fills the gap with the last value, + thereby avoiding the need to push back subsequent values. + Behaviour undefined if index outside [0, num-1]. + @since v0.99.4 + */ +static inline void ccCArrayFastRemoveValueAtIndex(ccCArray *arr, NSUInteger index) +{ + NSUInteger last = --arr->num; + arr->arr[index] = arr->arr[last]; +} + +/** Searches for the first occurance of value and removes it. If value is not found the function has no effect. + @since v0.99.4 + */ +static inline void ccCArrayRemoveValue(ccCArray *arr, void* value) +{ + NSUInteger index = ccCArrayGetIndexOfValue(arr, value); + if (index != NSNotFound) + ccCArrayRemoveValueAtIndex(arr, index); +} + +/** Removes from arr all values in minusArr. For each Value in minusArr, the first matching instance in arr will be removed. + @since v0.99.4 + */ +static inline void ccCArrayRemoveArray(ccCArray *arr, ccCArray *minusArr) +{ + for( NSUInteger i = 0; i < minusArr->num; i++) + ccCArrayRemoveValue(arr, minusArr->arr[i]); +} + +/** Removes from arr all values in minusArr. For each value in minusArr, all matching instances in arr will be removed. + @since v0.99.4 + */ +static inline void ccCArrayFullRemoveArray(ccCArray *arr, ccCArray *minusArr) +{ + NSUInteger back = 0; + + for( NSUInteger i = 0; i < arr->num; i++) { + if( ccCArrayContainsValue(minusArr, arr->arr[i]) ) { + back++; + } else + arr->arr[i - back] = arr->arr[i]; + } + + arr->num -= back; +} +#endif // CC_ARRAY_H \ No newline at end of file diff --git a/libs/cocos2d/Support/ccUtils.c b/libs/cocos2d/Support/ccUtils.c new file mode 100644 index 0000000..39786ec --- /dev/null +++ b/libs/cocos2d/Support/ccUtils.c @@ -0,0 +1,20 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + */ + +/* + ccNextPOT function is licensed under the same license that is used in CCTexture2D.m. + */ +#include "ccUtils.h" + +unsigned long ccNextPOT(unsigned long x) +{ + x = x - 1; + x = x | (x >> 1); + x = x | (x >> 2); + x = x | (x >> 4); + x = x | (x >> 8); + x = x | (x >>16); + return x + 1; +} \ No newline at end of file diff --git a/libs/cocos2d/Support/ccUtils.h b/libs/cocos2d/Support/ccUtils.h new file mode 100644 index 0000000..783fc54 --- /dev/null +++ b/libs/cocos2d/Support/ccUtils.h @@ -0,0 +1,29 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + */ + +#ifndef __CC_UTILS_H +#define __CC_UTILS_H + +/** @file ccUtils.h + Misc free functions + */ + +/* + ccNextPOT function is licensed under the same license that is used in CCTexture2D.m. + */ + +/** returns the Next Power of Two value. + + Examples: + - If "value" is 15, it will return 16. + - If "value" is 16, it will return 16. + - If "value" is 17, it will return 32. + + @since v0.99.5 + */ + +unsigned long ccNextPOT( unsigned long value ); + +#endif // ! __CC_UTILS_H diff --git a/libs/cocos2d/Support/uthash.h b/libs/cocos2d/Support/uthash.h new file mode 100644 index 0000000..a4bdc18 --- /dev/null +++ b/libs/cocos2d/Support/uthash.h @@ -0,0 +1,972 @@ +/* +Copyright (c) 2003-2010, Troy D. Hanson http://uthash.sourceforge.net +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#include /* memcmp,strlen */ +#include /* ptrdiff_t */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#ifdef _MSC_VER /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define DECLTYPE(x) +#endif +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while(0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while(0) +#endif + +/* a number of the hash function use uint32_t which isn't defined on win32 */ +#ifdef _MSC_VER +typedef unsigned int uint32_t; +#else +#include /* uint32_t */ +#endif + +#define UTHASH_VERSION 1.9.3 + +#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ + +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhe */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + unsigned _hf_bkt,_hf_hashv; \ + out=NULL; \ + if (head) { \ + HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ + keyptr,keylen,out); \ + } \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) +#define HASH_BLOOM_MAKE(tbl) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ + memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ +} while (0); + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0); + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#else +#define HASH_BLOOM_MAKE(tbl) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#endif + +#define HASH_MAKE_TABLE(hh,head) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ + sizeof(UT_hash_table)); \ + if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl->buckets, 0, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ +} while(0) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_bkt; \ + (add)->hh.next = NULL; \ + (add)->hh.key = (char*)keyptr; \ + (add)->hh.keylen = keylen_in; \ + if (!(head)) { \ + head = (add); \ + (head)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh,head); \ + } else { \ + (head)->hh.tbl->tail->next = (add); \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail = &((add)->hh); \ + } \ + (head)->hh.tbl->num_items++; \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ + (add)->hh.hashv, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ + HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ + HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ + HASH_FSCK(hh,head); \ +} while(0) + +#define HASH_TO_BKT( hashv, num_bkts, bkt ) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1)); \ +} while(0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ +do { \ + unsigned _hd_bkt; \ + struct UT_hash_handle *_hd_hh_del; \ + if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + head = NULL; \ + } else { \ + _hd_hh_del = &((delptr)->hh); \ + if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ + (head)->hh.tbl->tail = \ + (UT_hash_handle*)((char*)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho); \ + } \ + if ((delptr)->hh.prev) { \ + ((UT_hash_handle*)((char*)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ + } else { \ + DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ + } \ + if (_hd_hh_del->next) { \ + ((UT_hash_handle*)((char*)_hd_hh_del->next + \ + (head)->hh.tbl->hho))->prev = \ + _hd_hh_del->prev; \ + } \ + HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh,head); \ +} while (0) + + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ + HASH_FIND(hh,head,findstr,strlen(findstr),out) +#define HASH_ADD_STR(head,strfield,add) \ + HASH_ADD(hh,head,strfield,strlen(add->strfield),add) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head) \ +do { \ + unsigned _bkt_i; \ + unsigned _count, _bkt_count; \ + char *_prev; \ + struct UT_hash_handle *_thh; \ + if (head) { \ + _count = 0; \ + for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ + _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("invalid hh_prev %p, actual %p\n", \ + _thh->hh_prev, _prev ); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("invalid bucket count %d, actual %d\n", \ + (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid hh item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + /* traverse hh in app order; check next/prev integrity, count */ \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev !=(char*)(_thh->prev)) { \ + HASH_OOPS("invalid prev %p, actual %p\n", \ + _thh->prev, _prev ); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ + (head)->hh.tbl->hho) : NULL ); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid app item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ +#ifdef HASH_FUNCTION +#define HASH_FCN HASH_FUNCTION +#else +#define HASH_FCN HASH_JEN +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6 */ +#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hb_keylen=keylen; \ + char *_hb_key=(char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \ + bkt = (hashv) & (num_bkts-1); \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ +#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _sx_i; \ + char *_hs_key=(char*)(key); \ + hashv = 0; \ + for(_sx_i=0; _sx_i < keylen; _sx_i++) \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + bkt = hashv & (num_bkts-1); \ +} while (0) + +#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _fn_i; \ + char *_hf_key=(char*)(key); \ + hashv = 2166136261UL; \ + for(_fn_i=0; _fn_i < keylen; _fn_i++) \ + hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \ + bkt = hashv & (num_bkts-1); \ +} while(0); + +#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _ho_i; \ + char *_ho_key=(char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + char *_hj_key=(char*)(key); \ + hashv = 0xfeedbeef; \ + _hj_i = _hj_j = 0x9e3779b9; \ + _hj_k = keylen; \ + while (_hj_k >= 12) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12; \ + } \ + hashv += keylen; \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ + case 5: _hj_j += _hj_key[4]; \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ + case 1: _hj_i += _hj_key[0]; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ +do { \ + char *_sfh_key=(char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = keylen; \ + \ + int _sfh_rem = _sfh_len & 3; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabe; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ + bkt = hashv & (num_bkts-1); \ +} while(0); + +#ifdef HASH_USING_NO_STRICT_ALIASING +/* The MurmurHash exploits some CPU's (e.g. x86) tolerance for unaligned reads. + * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. + * So MurmurHash comes in two versions, the faster unaligned one and the slower + * aligned one. We only use the faster one on CPU's where we know it's safe. + * + * Note the preprocessor built-in defines can be emitted using: + * + * gcc -m64 -dM -E - < /dev/null (on gcc) + * cc -## a.c (where a.c is a simple test file) (Sun Studio) + */ +#if (defined(__i386__) || defined(__x86_64__)) +#define HASH_MUR HASH_MUR_UNALIGNED +#else +#define HASH_MUR HASH_MUR_ALIGNED +#endif + +/* Appleby's MurmurHash fast version for unaligned-tolerant archs like i386 */ +#define HASH_MUR_UNALIGNED(key,keylen,num_bkts,hashv,bkt) \ +do { \ + const unsigned int _mur_m = 0x5bd1e995; \ + const int _mur_r = 24; \ + hashv = 0xcafebabe ^ keylen; \ + char *_mur_key = (char *)(key); \ + uint32_t _mur_tmp, _mur_len = keylen; \ + \ + for (;_mur_len >= 4; _mur_len-=4) { \ + _mur_tmp = *(uint32_t *)_mur_key; \ + _mur_tmp *= _mur_m; \ + _mur_tmp ^= _mur_tmp >> _mur_r; \ + _mur_tmp *= _mur_m; \ + hashv *= _mur_m; \ + hashv ^= _mur_tmp; \ + _mur_key += 4; \ + } \ + \ + switch(_mur_len) \ + { \ + case 3: hashv ^= _mur_key[2] << 16; \ + case 2: hashv ^= _mur_key[1] << 8; \ + case 1: hashv ^= _mur_key[0]; \ + hashv *= _mur_m; \ + }; \ + \ + hashv ^= hashv >> 13; \ + hashv *= _mur_m; \ + hashv ^= hashv >> 15; \ + \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +/* Appleby's MurmurHash version for alignment-sensitive archs like Sparc */ +#define HASH_MUR_ALIGNED(key,keylen,num_bkts,hashv,bkt) \ +do { \ + const unsigned int _mur_m = 0x5bd1e995; \ + const int _mur_r = 24; \ + hashv = 0xcafebabe ^ (keylen); \ + char *_mur_key = (char *)(key); \ + uint32_t _mur_len = keylen; \ + int _mur_align = (int)_mur_key & 3; \ + \ + if (_mur_align && (_mur_len >= 4)) { \ + unsigned _mur_t = 0, _mur_d = 0; \ + switch(_mur_align) { \ + case 1: _mur_t |= _mur_key[2] << 16; \ + case 2: _mur_t |= _mur_key[1] << 8; \ + case 3: _mur_t |= _mur_key[0]; \ + } \ + _mur_t <<= (8 * _mur_align); \ + _mur_key += 4-_mur_align; \ + _mur_len -= 4-_mur_align; \ + int _mur_sl = 8 * (4-_mur_align); \ + int _mur_sr = 8 * _mur_align; \ + \ + for (;_mur_len >= 4; _mur_len-=4) { \ + _mur_d = *(unsigned *)_mur_key; \ + _mur_t = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \ + unsigned _mur_k = _mur_t; \ + _mur_k *= _mur_m; \ + _mur_k ^= _mur_k >> _mur_r; \ + _mur_k *= _mur_m; \ + hashv *= _mur_m; \ + hashv ^= _mur_k; \ + _mur_t = _mur_d; \ + _mur_key += 4; \ + } \ + _mur_d = 0; \ + if(_mur_len >= _mur_align) { \ + switch(_mur_align) { \ + case 3: _mur_d |= _mur_key[2] << 16; \ + case 2: _mur_d |= _mur_key[1] << 8; \ + case 1: _mur_d |= _mur_key[0]; \ + } \ + unsigned _mur_k = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \ + _mur_k *= _mur_m; \ + _mur_k ^= _mur_k >> _mur_r; \ + _mur_k *= _mur_m; \ + hashv *= _mur_m; \ + hashv ^= _mur_k; \ + _mur_k += _mur_align; \ + _mur_len -= _mur_align; \ + \ + switch(_mur_len) \ + { \ + case 3: hashv ^= _mur_key[2] << 16; \ + case 2: hashv ^= _mur_key[1] << 8; \ + case 1: hashv ^= _mur_key[0]; \ + hashv *= _mur_m; \ + } \ + } else { \ + switch(_mur_len) \ + { \ + case 3: _mur_d ^= _mur_key[2] << 16; \ + case 2: _mur_d ^= _mur_key[1] << 8; \ + case 1: _mur_d ^= _mur_key[0]; \ + case 0: hashv ^= (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \ + hashv *= _mur_m; \ + } \ + } \ + \ + hashv ^= hashv >> 13; \ + hashv *= _mur_m; \ + hashv ^= hashv >> 15; \ + } else { \ + for (;_mur_len >= 4; _mur_len-=4) { \ + unsigned _mur_k = *(unsigned*)_mur_key; \ + _mur_k *= _mur_m; \ + _mur_k ^= _mur_k >> _mur_r; \ + _mur_k *= _mur_m; \ + hashv *= _mur_m; \ + hashv ^= _mur_k; \ + _mur_key += 4; \ + } \ + switch(_mur_len) \ + { \ + case 3: hashv ^= _mur_key[2] << 16; \ + case 2: hashv ^= _mur_key[1] << 8; \ + case 1: hashv ^= _mur_key[0]; \ + hashv *= _mur_m; \ + } \ + \ + hashv ^= hashv >> 13; \ + hashv *= _mur_m; \ + hashv ^= hashv >> 15; \ + } \ + bkt = hashv & (num_bkts-1); \ +} while(0) +#endif /* HASH_USING_NO_STRICT_ALIASING */ + +/* key comparison function; return 0 if keys equal */ +#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ +do { \ + if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ + else out=NULL; \ + while (out) { \ + if (out->hh.keylen == keylen_in) { \ + if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break; \ + } \ + if (out->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,out->hh.hh_next)); \ + else out = NULL; \ + } \ +} while(0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,addhh) \ +do { \ + head.count++; \ + (addhh)->hh_next = head.hh_head; \ + (addhh)->hh_prev = NULL; \ + if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ + (head).hh_head=addhh; \ + if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ + && (addhh)->tbl->noexpand != 1) { \ + HASH_EXPAND_BUCKETS((addhh)->tbl); \ + } \ +} while(0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(hh,head,hh_del) \ + (head).count--; \ + if ((head).hh_head == hh_del) { \ + (head).hh_head = hh_del->hh_next; \ + } \ + if (hh_del->hh_prev) { \ + hh_del->hh_prev->hh_next = hh_del->hh_next; \ + } \ + if (hh_del->hh_next) { \ + hh_del->hh_next->hh_prev = hh_del->hh_prev; \ + } + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(tbl) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ + memset(_he_new_buckets, 0, \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + tbl->ideal_chain_maxlen = \ + (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ + ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ + tbl->nonideal_items = 0; \ + for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ + { \ + _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ + if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ + tbl->nonideal_items++; \ + _he_newbkt->expand_mult = _he_newbkt->count / \ + tbl->ideal_chain_maxlen; \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ + _he_thh; \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + tbl->num_buckets *= 2; \ + tbl->log2_num_buckets++; \ + tbl->buckets = _he_new_buckets; \ + tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ + (tbl->ineff_expands+1) : 0; \ + if (tbl->ineff_expands > 1) { \ + tbl->noexpand=1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ +} while(0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ + _hs_psize++; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + if (! (_hs_q) ) break; \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ + if (_hs_psize == 0) { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ + _hs_e = _hs_p; \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_psize--; \ + } else if (( \ + cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ + ) <= 0) { \ + _hs_e = _hs_p; \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail ) { \ + _hs_tail->next = ((_hs_e) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + _hs_e->prev = ((_hs_tail) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + _hs_tail->next = NULL; \ + if ( _hs_nmerges <= 1 ) { \ + _hs_looping=0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2; \ + } \ + HASH_FSCK(hh,head); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt=NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if (src) { \ + for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ + if (!dst) { \ + DECLTYPE_ASSIGN(dst,_elt); \ + HASH_MAKE_TABLE(hh_dst,dst); \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ + (dst)->hh_dst.tbl->num_items++; \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst,dst); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if (head) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)=NULL; \ + } \ +} while(0) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL)) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1 +#define HASH_BLOOM_SIGNATURE 0xb12220f2 + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + char bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/libs/cocos2d/Support/utlist.h b/libs/cocos2d/Support/utlist.h new file mode 100644 index 0000000..34c725b --- /dev/null +++ b/libs/cocos2d/Support/utlist.h @@ -0,0 +1,490 @@ +/* +Copyright (c) 2007-2010, Troy D. Hanson http://uthash.sourceforge.net +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTLIST_H +#define UTLIST_H + +#define UTLIST_VERSION 1.9.1 + +/* + * This file contains macros to manipulate singly and doubly-linked lists. + * + * 1. LL_ macros: singly-linked lists. + * 2. DL_ macros: doubly-linked lists. + * 3. CDL_ macros: circular doubly-linked lists. + * + * To use singly-linked lists, your structure must have a "next" pointer. + * To use doubly-linked lists, your structure must "prev" and "next" pointers. + * Either way, the pointer to the head of the list must be initialized to NULL. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *prev, *next; + * } + * + * struct item *list = NULL: + * + * int main() { + * struct item *item; + * ... allocate and populate item ... + * DL_APPEND(list, item); + * } + * -------------------------------------------------- + * + * For doubly-linked lists, the append and delete macros are O(1) + * For singly-linked lists, append and delete are O(n) but prepend is O(1) + * The sort macro is O(n log(n)) for all types of single/double/circular lists. + */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ code), this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#ifdef _MSC_VER /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define LDECLTYPE(x) decltype(x) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define LDECLTYPE(x) char* +#endif +#else /* GNU, Sun and other compilers */ +#define LDECLTYPE(x) __typeof(x) +#endif + +/* for VS2008 we use some workarounds to get around the lack of decltype, + * namely, we always reassign our tmp variable to the list head if we need + * to dereference its prev/next pointers, and save/restore the real head.*/ +#ifdef NO_DECLTYPE +#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define _NEXT(elt,list) ((char*)((list)->next)) +#define _NEXTASGN(elt,list,to) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +#define _PREV(elt,list) ((char*)((list)->prev)) +#define _PREVASGN(elt,list,to) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define _SV(elt,list) +#define _NEXT(elt,list) ((elt)->next) +#define _NEXTASGN(elt,list,to) ((elt)->next)=(to) +#define _PREV(elt,list) ((elt)->prev) +#define _PREVASGN(elt,list,to) ((elt)->prev)=(to) +#define _RS(list) +#define _CASTASGN(a,b) (a)=(b) +#endif + +/****************************************************************************** + * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * + * Unwieldy variable names used here to avoid shadowing passed-in variables. * + *****************************************************************************/ +#define LL_SORT(list, cmp) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } else _tmp=NULL; /* quiet gcc unused variable warning */ \ +} while (0) + +#define DL_SORT(list, cmp) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _CASTASGN(list->prev, _ls_tail); \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } else _tmp=NULL; /* quiet gcc unused variable warning */ \ +} while (0) + +#define CDL_SORT(list, cmp) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + LDECLTYPE(list) _tmp2; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); \ + if (_NEXT(_ls_q,list) == _ls_oldhead) { \ + _ls_q = NULL; \ + } else { \ + _ls_q = _NEXT(_ls_q,list); \ + } \ + _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _CASTASGN(list->prev,_ls_tail); \ + _CASTASGN(_tmp2,list); \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp2); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } else _tmp=NULL; /* quiet gcc unused variable warning */ \ +} while (0) + +/****************************************************************************** + * singly linked list macros (non-circular) * + *****************************************************************************/ +#define LL_PREPEND(head,add) \ +do { \ + (add)->next = head; \ + head = add; \ +} while (0) + +#define LL_APPEND(head,add) \ +do { \ + LDECLTYPE(head) _tmp; \ + (add)->next=NULL; \ + if (head) { \ + _tmp = head; \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(add); \ + } else { \ + (head)=(add); \ + } \ +} while (0) + +#define LL_DELETE(head,del) \ +do { \ + LDECLTYPE(head) _tmp; \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + _tmp = head; \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = ((del)->next); \ + } \ + } \ +} while (0) + +/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */ +#define LL_APPEND_VS2008(head,add) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#define LL_DELETE_VS2008(head,del) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while (head->next && (head->next != (del))) { \ + head = head->next; \ + } \ + if (head->next) { \ + head->next = ((del)->next); \ + } \ + { \ + char **_head_alias = (char**)&(head); \ + *_head_alias = _tmp; \ + } \ + } \ +} while (0) +#ifdef NO_DECLTYPE +#undef LL_APPEND +#define LL_APPEND LL_APPEND_VS2008 +#undef LL_DELETE +#define LL_DELETE LL_DELETE_VS2008 +#endif +/* end VS2008 replacements */ + +#define LL_FOREACH(head,el) \ + for(el=head;el;el=el->next) + +#define LL_FOREACH_SAFE(head,el,tmp) \ + for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + +#define LL_SEARCH_SCALAR(head,out,field,val) \ +do { \ + LL_FOREACH(head,out) { \ + if ((out)->field == (val)) break; \ + } \ +} while(0) + +#define LL_SEARCH(head,out,elt,cmp) \ +do { \ + LL_FOREACH(head,out) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while(0) + +/****************************************************************************** + * doubly linked list macros (non-circular) * + *****************************************************************************/ +#define DL_PREPEND(head,add) \ +do { \ + (add)->next = head; \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev = (add); \ + } else { \ + (add)->prev = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define DL_APPEND(head,add) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0); + +#define DL_DELETE(head,del) \ +do { \ + if ((del)->prev == (del)) { \ + (head)=NULL; \ + } else if ((del)==(head)) { \ + (del)->next->prev = (del)->prev; \ + (head) = (del)->next; \ + } else { \ + (del)->prev->next = (del)->next; \ + if ((del)->next) { \ + (del)->next->prev = (del)->prev; \ + } else { \ + (head)->prev = (del)->prev; \ + } \ + } \ +} while (0); + + +#define DL_FOREACH(head,el) \ + for(el=head;el;el=el->next) + +/* this version is safe for deleting the elements during iteration */ +#define DL_FOREACH_SAFE(head,el,tmp) \ + for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + +/* these are identical to their singly-linked list counterparts */ +#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR +#define DL_SEARCH LL_SEARCH + +/****************************************************************************** + * circular doubly linked list macros * + *****************************************************************************/ +#define CDL_PREPEND(head,add) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + } \ +(head)=(add); \ +} while (0) + +#define CDL_DELETE(head,del) \ +do { \ + if ( ((head)==(del)) && ((head)->next == (head))) { \ + (head) = 0L; \ + } else { \ + (del)->next->prev = (del)->prev; \ + (del)->prev->next = (del)->next; \ + if ((del) == (head)) (head)=(del)->next; \ + } \ +} while (0); + +#define CDL_FOREACH(head,el) \ + for(el=head;el;el=(el->next==head ? 0L : el->next)) + +#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ + for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \ + (el) && ((tmp2)=(el)->next, 1); \ + ((el) = (((el)==(tmp1)) ? 0L : (tmp2)))) + +#define CDL_SEARCH_SCALAR(head,out,field,val) \ +do { \ + CDL_FOREACH(head,out) { \ + if ((out)->field == (val)) break; \ + } \ +} while(0) + +#define CDL_SEARCH(head,out,elt,cmp) \ +do { \ + CDL_FOREACH(head,out) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while(0) + +#endif /* UTLIST_H */ + diff --git a/libs/cocos2d/ccConfig.h b/libs/cocos2d/ccConfig.h new file mode 100644 index 0000000..407c7da --- /dev/null +++ b/libs/cocos2d/ccConfig.h @@ -0,0 +1,281 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 + +/** + @file + cocos2d (cc) configuration file +*/ + +/** @def CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL + If enabled, the texture coordinates will be calculated by using this formula: + - texCoord.left = (rect.origin.x*2+1) / (texture.wide*2); + - texCoord.right = texCoord.left + (rect.size.width*2-2)/(texture.wide*2); + + The same for bottom and top. + + This formula prevents artifacts by using 99% of the texture. + The "correct" way to prevent artifacts is by using the spritesheet-artifact-fixer.py or a similar tool. + + Affected nodes: + - CCSprite / CCSpriteBatchNode and subclasses: CCBitmapFontAtlas, CCTMXTiledMap + - CCLabelAtlas + - CCQuadParticleSystem + - CCTileMap + + To enabled set it to 1. Disabled by default. + + @since v0.99.5 + */ +#define CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL 0 + + +/** @def CC_FONT_LABEL_SUPPORT + If enabled, FontLabel will be used to render .ttf files. + If the .ttf file is not found, then it will use the standard UIFont class + If disabled, the standard UIFont class will be used. + + To disable set it to 0. Enabled by default. + + Only valid for cocos2d-ios. Not supported on cocos2d-mac + */ +#define CC_FONT_LABEL_SUPPORT 1 + +/** @def CC_DIRECTOR_FAST_FPS + If enabled, then the FPS will be drawn using CCLabelAtlas (fast rendering). + You will need to add the fps_images.png to your project. + If disabled, the FPS will be rendered using CCLabel (slow rendering) + + To enable set it to a value different than 0. Enabled by default. + */ +#define CC_DIRECTOR_FAST_FPS 1 + +/** @def CC_DIRECTOR_FPS_INTERVAL + Senconds between FPS updates. + 0.5 seconds, means that the FPS number will be updated every 0.5 seconds. + Having a bigger number means a more reliable FPS + + Default value: 0.1f + */ +#define CC_DIRECTOR_FPS_INTERVAL (0.1f) + +/** @def CC_DIRECTOR_DISPATCH_FAST_EVENTS + If enabled, and only when it is used with CCFastDirector, the main loop will wait 0.04 seconds to + dispatch all the events, even if there are not events to dispatch. + If your game uses lot's of events (eg: touches) it might be a good idea to enable this feature. + Otherwise, it is safe to leave it disabled. + + To enable set it to 1. Disabled by default. + + @warning This feature is experimental + */ +#define CC_DIRECTOR_DISPATCH_FAST_EVENTS 0 + +/** @def CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD + If enabled, cocos2d-mac will run on the Display Link thread. If disabled cocos2d-mac will run in its own thread. + + If enabled, the images will be drawn at the "correct" time, but the events might not be very responsive. + If disabled, some frames might be skipped, but the events will be dispatched as they arrived. + + To enable set it to a 1, to disable it set to 0. Enabled by default. + + Only valid for cocos2d-mac. Not supported on cocos2d-ios. + + */ +#define CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD 1 + +/** @def CC_COCOSNODE_RENDER_SUBPIXEL + If enabled, the CCNode objects (CCSprite, CCLabel,etc) will be able to render in subpixels. + If disabled, integer pixels will be used. + + To enable set it to 1. Enabled by default. + */ +#define CC_COCOSNODE_RENDER_SUBPIXEL 1 + +/** @def CC_SPRITEBATCHNODE_RENDER_SUBPIXEL + If enabled, the CCSprite objects rendered with CCSpriteBatchNode will be able to render in subpixels. + If disabled, integer pixels will be used. + + To enable set it to 1. Enabled by default. + */ +#define CC_SPRITEBATCHNODE_RENDER_SUBPIXEL 1 + + +#if defined(__ARM_NEON__) || TARGET_IPHONE_SIMULATOR || defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +/** @def CC_USES_VBO + If enabled, batch nodes (texture atlas and particle system) will use VBO instead of vertex list (VBO is recommended by Apple) + + To enable set it to 1. + Enabled by default on iPhone with ARMv7 processors, iPhone Simulator and Mac + Disabled by default on iPhone with ARMv6 processors. + + @since v0.99.5 + */ +#define CC_USES_VBO 1 +#else +#define CC_USES_VBO 0 +#endif + +/** @def CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + If enabled, CCNode will transform the nodes using a cached Affine matrix. + If disabled, the node will be transformed using glTranslate,glRotate,glScale. + Using the affine matrix only requires 2 GL calls. + Using the translate/rotate/scale requires 5 GL calls. + But computing the Affine matrix is relative expensive. + But according to performance tests, Affine matrix performs better. + This parameter doesn't affect SpriteSheet nodes. + + To enable set it to a value different than 0. Enabled by default. + + */ +#define CC_NODE_TRANSFORM_USING_AFFINE_MATRIX 1 + +/** @def CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA + If most of your imamges have pre-multiplied alpha, set it to 1 (if you are going to use .PNG/.JPG file images). + Only set to 0 if ALL your images by-pass Apple UIImage loading system (eg: if you use libpng or PVR images) + + To enable set it to a value different than 0. Enabled by default. + + @since v0.99.5 + */ +#define CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA 1 + +/** @def CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP + Use GL_TRIANGLE_STRIP instead of GL_TRIANGLES when rendering the texture atlas. + It seems it is the recommend way, but it is much slower, so, enable it at your own risk + + To enable set it to a value different than 0. Disabled by default. + + */ +#define CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP 0 + + +/** @def CC_TEXTURE_NPOT_SUPPORT + If enabled, NPOT textures will be used where available. Only 3rd gen (and newer) devices support NPOT textures. + NPOT textures have the following limitations: + - They can't have mipmaps + - They only accept GL_CLAMP_TO_EDGE in GL_TEXTURE_WRAP_{S,T} + + To enable set it to a value different than 0. Disabled by default. + + @since v0.99.2 + */ +#define CC_TEXTURE_NPOT_SUPPORT 0 + +/** @def CC_RETINA_DISPLAY_SUPPORT + If enabled, cocos2d supports retina display. + For performance reasons, it's recommended disable it in games without retina display support, like iPad only games. + + To enable set it to 1. Use 0 to disable it. Enabled by default. + + @since v0.99.5 + */ +#define CC_RETINA_DISPLAY_SUPPORT 1 + +/** @def CC_RETINA_DISPLAY_FILENAME_SUFFIX + It's the suffix that will be appended to the files in order to load "retina display" images. + + On an iPhone4 with Retina Display support enabled, the file @"sprite-hd.png" will be loaded instead of @"sprite.png". + If the file doesn't exist it will use the non-retina display image. + + Platforms: Only used on Retina Display devices like iPhone 4. + + @since v0.99.5 + */ +#define CC_RETINA_DISPLAY_FILENAME_SUFFIX @"-hd" + +/** @def CC_USE_RGBA32_LABELS_ON_NEON_ARCH + If enabled, it will use RGBA8888 (32-bit textures) on Neon devices for CCLabelTTF objects. + If it is disabled, or if it is used on another architecture it will use A8 (8-bit textures). + On Neon devices, RGBA8888 textures are 6% faster than A8 textures, but then will consule 4x memory. + + This feature is disabled by default. + + Platforms: Only used on ARM Neon architectures like iPhone 3GS or newer and iPad. + + @since v0.99.5 + */ +#define CC_USE_RGBA32_LABELS_ON_NEON_ARCH 0 + +/** @def CC_SPRITE_DEBUG_DRAW + If enabled, all subclasses of CCSprite will draw a bounding box + Useful for debugging purposes only. It is recommened to leave it disabled. + + To enable set it to a value different than 0. Disabled by default. + */ +#define CC_SPRITE_DEBUG_DRAW 0 + +/** @def CC_SPRITEBATCHNODE_DEBUG_DRAW + If enabled, all subclasses of CCSprite that are rendered using an CCSpriteBatchNode draw a bounding box. + Useful for debugging purposes only. It is recommened to leave it disabled. + + To enable set it to a value different than 0. Disabled by default. + */ +#define CC_SPRITEBATCHNODE_DEBUG_DRAW 0 + +/** @def CC_LABELBMFONT_DEBUG_DRAW + If enabled, all subclasses of CCLabelBMFont will draw a bounding box + Useful for debugging purposes only. It is recommened to leave it disabled. + + To enable set it to a value different than 0. Disabled by default. + */ +#define CC_LABELBMFONT_DEBUG_DRAW 0 + +/** @def CC_LABELBMFONT_DEBUG_DRAW + If enabled, all subclasses of CCLabeltAtlas will draw a bounding box + Useful for debugging purposes only. It is recommened to leave it disabled. + + To enable set it to a value different than 0. Disabled by default. + */ +#define CC_LABELATLAS_DEBUG_DRAW 0 + +/** @def CC_ENABLE_PROFILERS + If enabled, will activate various profilers withing cocos2d. This statistical data will be output to the console + once per second showing average time (in milliseconds) required to execute the specific routine(s). + Useful for debugging purposes only. It is recommened to leave it disabled. + + To enable set it to a value different than 0. Disabled by default. + */ +#define CC_ENABLE_PROFILERS 0 + +// +// DON'T edit this macro. +// +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +#if CC_RETINA_DISPLAY_SUPPORT +#define CC_IS_RETINA_DISPLAY_SUPPORTED 1 +#else +#define CC_IS_RETINA_DISPLAY_SUPPORTED 0 +#endif + +#elif __MAC_OS_X_VERSION_MAX_ALLOWED + +#define CC_IS_RETINA_DISPLAY_SUPPORTED 0 + +#endif + + diff --git a/libs/cocos2d/ccMacros.h b/libs/cocos2d/ccMacros.h new file mode 100644 index 0000000..56853c8 --- /dev/null +++ b/libs/cocos2d/ccMacros.h @@ -0,0 +1,252 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 +#import "ccConfig.h" + +#import +#import + +/** + @file + cocos2d helper macros + */ + +/* + * if COCOS2D_DEBUG is not defined, or if it is 0 then + * all CCLOGXXX macros will be disabled + * + * if COCOS2D_DEBUG==1 then: + * CCLOG() will be enabled + * CCLOGERROR() will be enabled + * CCLOGINFO() will be disabled + * + * if COCOS2D_DEBUG==2 or higher then: + * CCLOG() will be enabled + * CCLOGERROR() will be enabled + * CCLOGINFO() will be enabled + */ +#if !defined(COCOS2D_DEBUG) || COCOS2D_DEBUG == 0 +#define CCLOG(...) do {} while (0) +#define CCLOGINFO(...) do {} while (0) +#define CCLOGERROR(...) do {} while (0) + +#elif COCOS2D_DEBUG == 1 +#define CCLOG(...) NSLog(__VA_ARGS__) +#define CCLOGERROR(...) NSLog(__VA_ARGS__) +#define CCLOGINFO(...) do {} while (0) + +#elif COCOS2D_DEBUG > 1 +#define CCLOG(...) NSLog(__VA_ARGS__) +#define CCLOGERROR(...) NSLog(__VA_ARGS__) +#define CCLOGINFO(...) NSLog(__VA_ARGS__) +#endif // COCOS2D_DEBUG + +/** @def CC_SWAP +simple macro that swaps 2 variables +*/ +#define CC_SWAP( x, y ) \ +({ __typeof__(x) temp = (x); \ + x = y; y = temp; \ +}) + + +/** @def CCRANDOM_MINUS1_1 + returns a random float between -1 and 1 + */ +#define CCRANDOM_MINUS1_1() ((random() / (float)0x3fffffff )-1.0f) + +/** @def CCRANDOM_0_1 + returns a random float between 0 and 1 + */ +#define CCRANDOM_0_1() ((random() / (float)0x7fffffff )) + +/** @def CC_DEGREES_TO_RADIANS + converts degrees to radians + */ +#define CC_DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) * 0.01745329252f) // PI / 180 + +/** @def CC_RADIANS_TO_DEGREES + converts radians to degrees + */ +#define CC_RADIANS_TO_DEGREES(__ANGLE__) ((__ANGLE__) * 57.29577951f) // PI * 180 + +/** @def CC_BLEND_SRC +default gl blend src function. Compatible with premultiplied alpha images. +*/ +#if CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA +#define CC_BLEND_SRC GL_ONE +#define CC_BLEND_DST GL_ONE_MINUS_SRC_ALPHA +#else +#define CC_BLEND_SRC GL_SRC_ALPHA +#define CC_BLEND_DST GL_ONE_MINUS_SRC_ALPHA +#endif // ! CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA + +/** @def CC_ENABLE_DEFAULT_GL_STATES + GL states that are enabled: + - GL_TEXTURE_2D + - GL_VERTEX_ARRAY + - GL_TEXTURE_COORD_ARRAY + - GL_COLOR_ARRAY + */ +#define CC_ENABLE_DEFAULT_GL_STATES() { \ + glEnableClientState(GL_VERTEX_ARRAY); \ + glEnableClientState(GL_COLOR_ARRAY); \ + glEnableClientState(GL_TEXTURE_COORD_ARRAY); \ + glEnable(GL_TEXTURE_2D); \ +} + +/** @def CC_DISABLE_DEFAULT_GL_STATES + Disable default GL states: + - GL_TEXTURE_2D + - GL_VERTEX_ARRAY + - GL_TEXTURE_COORD_ARRAY + - GL_COLOR_ARRAY + */ +#define CC_DISABLE_DEFAULT_GL_STATES() { \ + glDisable(GL_TEXTURE_2D); \ + glDisableClientState(GL_TEXTURE_COORD_ARRAY); \ + glDisableClientState(GL_COLOR_ARRAY); \ + glDisableClientState(GL_VERTEX_ARRAY); \ +} + +/** @def CC_DIRECTOR_INIT + - Initializes an EAGLView with 0-bit depth format, and RGB565 render buffer. + - The EAGLView view will have multiple touches disabled. + - It will create a UIWindow and it will assign it the 'window' variable. 'window' must be declared before calling this marcro. + - It will parent the EAGLView to the created window + - If the firmware >= 3.1 it will create a Display Link Director. Else it will create an NSTimer director. + - It will try to run at 60 FPS. + - The FPS won't be displayed. + - The orientation will be portrait. + - It will connect the director with the EAGLView. + + IMPORTANT: If you want to use another type of render buffer (eg: RGBA8) + or if you want to use a 16-bit or 24-bit depth buffer, you should NOT + use this macro. Instead, you should create the EAGLView manually. + + @since v0.99.4 + */ + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +#define CC_DIRECTOR_INIT() \ +do { \ + window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; \ + if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] ) \ + [CCDirector setDirectorType:kCCDirectorTypeNSTimer]; \ + CCDirector *__director = [CCDirector sharedDirector]; \ + [__director setDeviceOrientation:kCCDeviceOrientationPortrait]; \ + [__director setDisplayFPS:NO]; \ + [__director setAnimationInterval:1.0/60]; \ + EAGLView *__glView = [EAGLView viewWithFrame:[window bounds] \ + pixelFormat:kEAGLColorFormatRGB565 \ + depthFormat:0 /* GL_DEPTH_COMPONENT24_OES */ \ + preserveBackbuffer:NO \ + sharegroup:nil \ + multiSampling:NO \ + numberOfSamples:0 \ + ]; \ + [__director setOpenGLView:__glView]; \ + [window addSubview:__glView]; \ + [window makeKeyAndVisible]; \ +} while(0) + + +#elif __MAC_OS_X_VERSION_MAX_ALLOWED + +#import "Platforms/Mac/MacWindow.h" + +#define CC_DIRECTOR_INIT(__WINSIZE__) \ +do { \ + NSRect frameRect = NSMakeRect(0, 0, (__WINSIZE__).width, (__WINSIZE__).height); \ + self.window = [[MacWindow alloc] initWithFrame:frameRect fullscreen:NO]; \ + self.glView = [[MacGLView alloc] initWithFrame:frameRect shareContext:nil]; \ + [self.window setContentView:self.glView]; \ + CCDirector *__director = [CCDirector sharedDirector]; \ + [__director setDisplayFPS:NO]; \ + [__director setOpenGLView:self.glView]; \ + [(CCDirectorMac*)__director setOriginalWinSize:__WINSIZE__]; \ + [self.window makeMainWindow]; \ + [self.window makeKeyAndOrderFront:self]; \ +} while(0) + +#endif + + + /** @def CC_DIRECTOR_END + Stops and removes the director from memory. + Removes the EAGLView from its parent + + @since v0.99.4 + */ +#define CC_DIRECTOR_END() \ +do { \ + CCDirector *__director = [CCDirector sharedDirector]; \ + CC_GLVIEW *__view = [__director openGLView]; \ + [__view removeFromSuperview]; \ + [__director end]; \ +} while(0) + + +#if CC_IS_RETINA_DISPLAY_SUPPORTED + +/****************************/ +/** RETINA DISPLAY ENABLED **/ +/****************************/ + +/** @def CC_CONTENT_SCALE_FACTOR + On Mac it returns 1; + On iPhone it returns 2 if RetinaDisplay is On. Otherwise it returns 1 + */ +#import "Platforms/iOS/CCDirectorIOS.h" +#define CC_CONTENT_SCALE_FACTOR() __ccContentScaleFactor + + +/** @def CC_RECT_PIXELS_TO_POINTS + Converts a rect in pixels to points + */ +#define CC_RECT_PIXELS_TO_POINTS(__pixels__) \ + CGRectMake( (__pixels__).origin.x / CC_CONTENT_SCALE_FACTOR(), (__pixels__).origin.y / CC_CONTENT_SCALE_FACTOR(), \ + (__pixels__).size.width / CC_CONTENT_SCALE_FACTOR(), (__pixels__).size.height / CC_CONTENT_SCALE_FACTOR() ) + +/** @def CC_RECT_POINTS_TO_PIXELS + Converts a rect in points to pixels + */ +#define CC_RECT_POINTS_TO_PIXELS(__points__) \ + CGRectMake( (__points__).origin.x * CC_CONTENT_SCALE_FACTOR(), (__points__).origin.y * CC_CONTENT_SCALE_FACTOR(), \ + (__points__).size.width * CC_CONTENT_SCALE_FACTOR(), (__points__).size.height * CC_CONTENT_SCALE_FACTOR() ) + +#else // retina disabled + +/*****************************/ +/** RETINA DISPLAY DISABLED **/ +/*****************************/ + +#define CC_CONTENT_SCALE_FACTOR() 1 +#define CC_RECT_PIXELS_TO_POINTS(__pixels__) __pixels__ +#define CC_RECT_POINTS_TO_PIXELS(__points__) __points__ + +#endif // CC_IS_RETINA_DISPLAY_SUPPORTED diff --git a/libs/cocos2d/ccTypes.h b/libs/cocos2d/ccTypes.h new file mode 100644 index 0000000..4b69c4a --- /dev/null +++ b/libs/cocos2d/ccTypes.h @@ -0,0 +1,286 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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. + */ + + +/** + @file + cocos2d (cc) types +*/ + +#import +#import + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import // CGPoint +#endif + +#import "Platforms/CCGL.h" + +/** RGB color composed of bytes 3 bytes +@since v0.8 + */ +typedef struct _ccColor3B +{ + GLubyte r; + GLubyte g; + GLubyte b; +} ccColor3B; + +//! helper macro that creates an ccColor3B type +static inline ccColor3B +ccc3(const GLubyte r, const GLubyte g, const GLubyte b) +{ + ccColor3B c = {r, g, b}; + return c; +} +//ccColor3B predefined colors +//! White color (255,255,255) +static const ccColor3B ccWHITE = {255,255,255}; +//! Yellow color (255,255,0) +static const ccColor3B ccYELLOW = {255,255,0}; +//! Blue color (0,0,255) +static const ccColor3B ccBLUE = {0,0,255}; +//! Green Color (0,255,0) +static const ccColor3B ccGREEN = {0,255,0}; +//! Red Color (255,0,0,) +static const ccColor3B ccRED = {255,0,0}; +//! Magenta Color (255,0,255) +static const ccColor3B ccMAGENTA = {255,0,255}; +//! Black Color (0,0,0) +static const ccColor3B ccBLACK = {0,0,0}; +//! Orange Color (255,127,0) +static const ccColor3B ccORANGE = {255,127,0}; +//! Gray Color (166,166,166) +static const ccColor3B ccGRAY = {166,166,166}; + +/** RGBA color composed of 4 bytes +@since v0.8 +*/ +typedef struct _ccColor4B +{ + GLubyte r; + GLubyte g; + GLubyte b; + GLubyte a; +} ccColor4B; +//! helper macro that creates an ccColor4B type +static inline ccColor4B +ccc4(const GLubyte r, const GLubyte g, const GLubyte b, const GLubyte o) +{ + ccColor4B c = {r, g, b, o}; + return c; +} + + +/** RGBA color composed of 4 floats +@since v0.8 +*/ +typedef struct _ccColor4F { + GLfloat r; + GLfloat g; + GLfloat b; + GLfloat a; +} ccColor4F; + +/** Returns a ccColor4F from a ccColor3B. Alpha will be 1. + @since v0.99.1 + */ +static inline ccColor4F ccc4FFromccc3B(ccColor3B c) +{ + return (ccColor4F){c.r/255.f, c.g/255.f, c.b/255.f, 1.f}; +} + +/** Returns a ccColor4F from a ccColor4B. + @since v0.99.1 + */ +static inline ccColor4F ccc4FFromccc4B(ccColor4B c) +{ + return (ccColor4F){c.r/255.f, c.g/255.f, c.b/255.f, c.a/255.f}; +} + +/** returns YES if both ccColor4F are equal. Otherwise it returns NO. + @since v0.99.1 + */ +static inline BOOL ccc4FEqual(ccColor4F a, ccColor4F b) +{ + return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; +} + +/** A vertex composed of 2 GLfloats: x, y + @since v0.8 + */ +typedef struct _ccVertex2F +{ + GLfloat x; + GLfloat y; +} ccVertex2F; + +/** A vertex composed of 2 floats: x, y + @since v0.8 + */ +typedef struct _ccVertex3F +{ + GLfloat x; + GLfloat y; + GLfloat z; +} ccVertex3F; + +/** A texcoord composed of 2 floats: u, y + @since v0.8 + */ +typedef struct _ccTex2F { + GLfloat u; + GLfloat v; +} ccTex2F; + + +//! Point Sprite component +typedef struct _ccPointSprite +{ + ccVertex2F pos; // 8 bytes + ccColor4B color; // 4 bytes + GLfloat size; // 4 bytes +} ccPointSprite; + +//! A 2D Quad. 4 * 2 floats +typedef struct _ccQuad2 { + ccVertex2F tl; + ccVertex2F tr; + ccVertex2F bl; + ccVertex2F br; +} ccQuad2; + + +//! A 3D Quad. 4 * 3 floats +typedef struct _ccQuad3 { + ccVertex3F bl; + ccVertex3F br; + ccVertex3F tl; + ccVertex3F tr; +} ccQuad3; + +//! A 2D grid size +typedef struct _ccGridSize +{ + NSInteger x; + NSInteger y; +} ccGridSize; + +//! helper function to create a ccGridSize +static inline ccGridSize +ccg(const NSInteger x, const NSInteger y) +{ + ccGridSize v = {x, y}; + return v; +} + +//! a Point with a vertex point, a tex coord point and a color 4B +typedef struct _ccV2F_C4B_T2F +{ + //! vertices (2F) + ccVertex2F vertices; + //! colors (4B) + ccColor4B colors; + //! tex coords (2F) + ccTex2F texCoords; +} ccV2F_C4B_T2F; + +//! a Point with a vertex point, a tex coord point and a color 4F +typedef struct _ccV2F_C4F_T2F +{ + //! vertices (2F) + ccVertex2F vertices; + //! colors (4F) + ccColor4F colors; + //! tex coords (2F) + ccTex2F texCoords; +} ccV2F_C4F_T2F; + +//! a Point with a vertex point, a tex coord point and a color 4B +typedef struct _ccV3F_C4B_T2F +{ + //! vertices (3F) + ccVertex3F vertices; // 12 bytes +// char __padding__[4]; + + //! colors (4B) + ccColor4B colors; // 4 bytes +// char __padding2__[4]; + + // tex coords (2F) + ccTex2F texCoords; // 8 byts +} ccV3F_C4B_T2F; + +//! 4 ccVertex2FTex2FColor4B Quad +typedef struct _ccV2F_C4B_T2F_Quad +{ + //! bottom left + ccV2F_C4B_T2F bl; + //! bottom right + ccV2F_C4B_T2F br; + //! top left + ccV2F_C4B_T2F tl; + //! top right + ccV2F_C4B_T2F tr; +} ccV2F_C4B_T2F_Quad; + +//! 4 ccVertex3FTex2FColor4B +typedef struct _ccV3F_C4B_T2F_Quad +{ + //! top left + ccV3F_C4B_T2F tl; + //! bottom left + ccV3F_C4B_T2F bl; + //! top right + ccV3F_C4B_T2F tr; + //! bottom right + ccV3F_C4B_T2F br; +} ccV3F_C4B_T2F_Quad; + +//! 4 ccVertex2FTex2FColor4F Quad +typedef struct _ccV2F_C4F_T2F_Quad +{ + //! bottom left + ccV2F_C4F_T2F bl; + //! bottom right + ccV2F_C4F_T2F br; + //! top left + ccV2F_C4F_T2F tl; + //! top right + ccV2F_C4F_T2F tr; +} ccV2F_C4F_T2F_Quad; + +//! Blend Function used for textures +typedef struct _ccBlendFunc +{ + //! source blend function + GLenum src; + //! destination blend function + GLenum dst; +} ccBlendFunc; + +//! delta time type +//! if you want more resolution redefine it as a double +typedef float ccTime; +//typedef double ccTime; diff --git a/libs/cocos2d/cocos2d.h b/libs/cocos2d/cocos2d.h new file mode 100644 index 0000000..4769732 --- /dev/null +++ b/libs/cocos2d/cocos2d.h @@ -0,0 +1,160 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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. + */ + +/** @mainpage cocos2d for iPhone API reference + * + * @image html Icon.png + * + * @section intro Introduction + * This is cocos2d API reference + * + * The programming guide is hosted here: http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:index + * + *
+ * + * @todo A native english speaker should check the grammar. We need your help! + * + */ + +// 0x00 HI ME LO +// 00 01 00 00 +#define COCOS2D_VERSION 0x00010000 + +#import + +// +// all cocos2d include files +// +#import "ccConfig.h" // should be included first + +#import "CCActionManager.h" +#import "CCAction.h" +#import "CCActionInstant.h" +#import "CCActionInterval.h" +#import "CCActionEase.h" +#import "CCActionCamera.h" +#import "CCActionTween.h" +#import "CCActionEase.h" +#import "CCActionTiledGrid.h" +#import "CCActionGrid3D.h" +#import "CCActionGrid.h" +#import "CCActionProgressTimer.h" +#import "CCActionPageTurn3D.h" + +#import "CCAnimation.h" +#import "CCAnimationCache.h" +#import "CCSprite.h" +#import "CCSpriteFrame.h" +#import "CCSpriteBatchNode.h" +#import "CCSpriteFrameCache.h" + +#import "CCLabelTTF.h" +#import "CCLabelBMFont.h" +#import "CCLabelAtlas.h" + +#import "CCParticleSystem.h" +#import "CCParticleSystemPoint.h" +#import "CCParticleSystemQuad.h" +#import "CCParticleExamples.h" + +#import "CCTexture2D.h" +#import "CCTexturePVR.h" +#import "CCTextureCache.h" +#import "CCTextureAtlas.h" + +#import "CCTransition.h" +#import "CCTransitionPageTurn.h" +#import "CCTransitionRadial.h" + +#import "CCTMXTiledMap.h" +#import "CCTMXLayer.h" +#import "CCTMXObjectGroup.h" +#import "CCTMXXMLParser.h" +#import "CCTileMapAtlas.h" + +#import "CCLayer.h" +#import "CCMenu.h" +#import "CCMenuItem.h" +#import "CCDrawingPrimitives.h" +#import "CCScene.h" +#import "CCScheduler.h" +#import "CCBlockSupport.h" +#import "CCCamera.h" +#import "CCProtocols.h" +#import "CCNode.h" +#import "CCDirector.h" +#import "CCAtlasNode.h" +#import "CCGrabber.h" +#import "CCGrid.h" +#import "CCParallaxNode.h" +#import "CCRenderTexture.h" +#import "CCMotionStreak.h" +#import "CCConfiguration.h" + +// +// cocos2d macros +// +#import "ccTypes.h" +#import "ccMacros.h" + + +// Platform common +#import "Platforms/CCGL.h" +#import "Platforms/CCNS.h" + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "Platforms/iOS/CCTouchDispatcher.h" +#import "Platforms/iOS/CCTouchDelegateProtocol.h" +#import "Platforms/iOS/CCTouchHandler.h" +#import "Platforms/iOS/EAGLView.h" +#import "Platforms/iOS/CCDirectorIOS.h" + +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import "Platforms/Mac/MacGLView.h" +#import "Platforms/Mac/CCDirectorMac.h" +#endif + +// +// cocos2d helper files +// +#import "Support/OpenGL_Internal.h" +#import "Support/CCFileUtils.h" +#import "Support/CGPointExtension.h" +#import "Support/ccCArray.h" +#import "Support/CCArray.h" +#import "Support/ccUtils.h" + +#if CC_ENABLE_PROFILERS +#import "Support/CCProfiling.h" +#endif // CC_ENABLE_PROFILERS + + +// free functions +NSString * cocos2dVersion(void); + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifndef __IPHONE_4_0 +#error "If you are targeting iPad, you should set BASE SDK = 4.0 (or 4.1, or 4.2), and set the 'iOS deploy target' = 3.2" +#endif +#endif diff --git a/libs/cocos2d/cocos2d.m b/libs/cocos2d/cocos2d.m new file mode 100644 index 0000000..625bdea --- /dev/null +++ b/libs/cocos2d/cocos2d.m @@ -0,0 +1,33 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 + +static NSString *version = @"cocos2d v1.0.0-rc"; + +NSString *cocos2dVersion() +{ + return version; +} diff --git a/libs/cocoslive/CLScoreServerPost.h b/libs/cocoslive/CLScoreServerPost.h new file mode 100644 index 0000000..3954fa4 --- /dev/null +++ b/libs/cocoslive/CLScoreServerPost.h @@ -0,0 +1,141 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 + +// for MD5 signing +#import + +// cocoslive definitions +#import "cocoslive.h" + +// Score Server protocol version +#define SCORE_SERVER_PROTOCOL_VERSION @"1.1" + +// Server URL +#ifdef USE_LOCAL_SERVER +#define SCORE_SERVER_SEND_URL @"http://localhost:8080/api/post-score" +#define SCORE_SERVER_UPDATE_URL @"http://localhost:8080/api/update-score" +#else +#define SCORE_SERVER_SEND_URL @"http://www.cocoslive.net/api/post-score" +#define SCORE_SERVER_UPDATE_URL @"http://www.cocoslive.net/api/update-score" +#endif + +/// Type of errors from the Post Score request +typedef enum { + /// post request successful + kPostStatusOK = 0, + /// post request failed to establish a connection. wi-fi isn't enabled. + /// Don't retry when this option is preset + kPostStatusConnectionFailed = 1, + /// post request failed to post the score. Server might be busy. + /// Retry is suggested + kPostStatusPostFailed = 2, +} tPostStatus; + +enum { + //! Invalid Ranking. Valid rankins are from 1 to ... + kServerPostInvalidRanking = 0, +}; + +/** + * Handles the Score Post to the cocos live server + */ +@interface CLScoreServerPost : NSObject { + /// game key. secret shared with the server. + /// used to sign the values to prevent spoofing. + NSString *gameKey; + + /// game name, used as a login name. + NSString *gameName; + + /// delegate instance of fetch score + id delegate; + + /// ranking + NSUInteger ranking_; + + /// score was updated + BOOL scoreDidUpdate_; + + /// data received + NSMutableData *receivedData; + + /// values to send in the POST + NSMutableArray *bodyValues; + + /// status of the request + tPostStatus postStatus_; + + /// mdt context + CC_MD5_CTX md5Ctx; + + /// the connection + NSURLConnection *connection_; +} + +/** status from the score post */ +@property (nonatomic,readonly) tPostStatus postStatus; + +/** connection to the server */ +@property (nonatomic, retain) NSURLConnection *connection; + +/** ranking of your score + @since v0.7.3 + */ +@property (nonatomic,readonly) NSUInteger ranking; + +/** whether or not the score was updated + @since v0.7.3 + */ +@property (nonatomic,readonly) BOOL scoreDidUpdate; + +/** creates a cocos server with a game name and a game key */ ++(id) serverWithGameName:(NSString*) name gameKey:(NSString*) key delegate:(id)delegate; + +/** initializes a cocos server with a game name and a game key */ +-(id) initWithGameName:(NSString*) name gameKey:(NSString*) key delegate:(id)delegate; + +/** send the scores to the server. A new entre will be created on the server */ +-(BOOL) sendScore: (NSDictionary*) dict; + +/** + * Sends a score dictionary to the server for updating an existing entry by playername and device id, or creating a new one. + * The passed dictionary must contain a cc_playername key, otherwise it will raise and exception. + * @since v0.7.1 + */ +-(BOOL) updateScore: (NSDictionary*) dict; + +@end + +/** CocosLivePost protocol */ +@protocol CLPostDelegate +/** callback method that will be called if the post is successful */ +-(void) scorePostOk:(id) sender; +/** callback method that will be called if the post fails */ +-(void) scorePostFail:(id) sender; +@end diff --git a/libs/cocoslive/CLScoreServerPost.m b/libs/cocoslive/CLScoreServerPost.m new file mode 100644 index 0000000..e5a0388 --- /dev/null +++ b/libs/cocoslive/CLScoreServerPost.m @@ -0,0 +1,332 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 "CLScoreServerPost.h" +#import "ccMacros.h" + +// free function used to sort +NSInteger alphabeticSort(id string1, id string2, void *reverse) +{ + if ((NSInteger *)reverse == NO) + return [string2 localizedCaseInsensitiveCompare:string1]; + return [string1 localizedCaseInsensitiveCompare:string2]; +} + + +@interface CLScoreServerPost (Private) +-(void) addValue:(NSString*)value key:(NSString*)key; +-(void) calculateHashAndAddValue:(id)value key:(NSString*)key; +-(NSString*) getHashForData; +-(NSData*) getBodyValues; +-(NSString*) encodeData:(NSString*)data; +-(NSMutableURLRequest *) scoreServerRequestWithURLString:(NSString *)url; +-(BOOL) submitScore:(NSDictionary*)dict forUpdate:(BOOL)isUpdate; +@end + + +@implementation CLScoreServerPost + +@synthesize postStatus = postStatus_; +@synthesize ranking = ranking_; +@synthesize scoreDidUpdate = scoreDidUpdate_; +@synthesize connection = connection_; + ++(id) serverWithGameName:(NSString*) name gameKey:(NSString*) key delegate:(id) delegate +{ + return [[[self alloc] initWithGameName:name gameKey:key delegate:delegate] autorelease]; +} + +-(id) initWithGameName:(NSString*) name gameKey:(NSString*) key delegate:(id)aDelegate +{ + self = [super init]; + if( self ) { + gameKey = [key retain]; + gameName = [name retain]; + bodyValues = [[NSMutableArray arrayWithCapacity:5] retain]; + delegate = [aDelegate retain]; + receivedData = [[NSMutableData data] retain]; + + ranking_ = kServerPostInvalidRanking; + } + + return self; +} + +-(void) dealloc +{ + CCLOGINFO(@"deallocing %@", self); + [delegate release]; + [gameKey release]; + [gameName release]; + [bodyValues release]; + [receivedData release]; + [connection_ release]; + [super dealloc]; +} + + +#pragma mark ScoreServer send scores +-(BOOL) sendScore: (NSDictionary*) dict +{ + return [self submitScore:dict forUpdate:NO]; +} + +-(BOOL) updateScore: (NSDictionary*) dict +{ + if (![dict objectForKey:@"cc_playername"]) { + // fail. cc_playername + cc_device_id are needed to update an score + [NSException raise:@"cocosLive:updateScore" format:@"cc_playername not found"]; + } + return [self submitScore:dict forUpdate:YES]; +} + +-(BOOL) submitScore: (NSDictionary*)dict forUpdate:(BOOL)isUpdate +{ + [receivedData setLength:0]; + [bodyValues removeAllObjects]; + + // reset status + postStatus_ = kPostStatusOK; + + // create the request + NSMutableURLRequest *post = [self scoreServerRequestWithURLString:(isUpdate ? SCORE_SERVER_UPDATE_URL : SCORE_SERVER_SEND_URL)]; + + CC_MD5_Init( &md5Ctx); + + // hash SHALL be calculated in certain order + NSArray *keys = [dict allKeys]; + int reverseSort = NO; + NSArray *sortedKeys = [keys sortedArrayUsingFunction:alphabeticSort context:&reverseSort]; + for( id key in sortedKeys ) + [self calculateHashAndAddValue:[dict objectForKey:key] key:key]; + + // device id is hashed to prevent spoofing this same score from different devices + // one way to prevent a replay attack is to send cc_id & cc_time and use it as primary keys + + [self addValue:[[UIDevice currentDevice] uniqueIdentifier] key:@"cc_device_id"]; + [self addValue:gameName key:@"cc_gamename"]; + [self addValue:[self getHashForData] key:@"cc_hash"]; + [self addValue:SCORE_SERVER_PROTOCOL_VERSION key:@"cc_prot_ver"]; + + [post setHTTPBody: [self getBodyValues] ]; + + // create the connection with the request + // and start loading the data + self.connection=[[NSURLConnection alloc] initWithRequest:post delegate:self]; + + if ( ! connection_) + return NO; + + return YES; +} + +-(NSMutableURLRequest *) scoreServerRequestWithURLString:(NSString *)url { + NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:[NSURL URLWithString: url] + cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:10.0]; + + [request setHTTPMethod: @"POST"]; + [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; + return request; +} + +-(void) calculateHashAndAddValue:(id) value key:(NSString*) key +{ + NSString *val; + // value shall be a string or nsnumber + if( [value respondsToSelector:@selector(stringValue)] ) + val = [value stringValue]; + else if( [value isKindOfClass:[NSString class]] ) + val = value; + else + [NSException raise:@"Invalid format for value" format:@"Invalid format for value. addValue"]; + + [self addValue:val key:key]; + + const char * data = [val UTF8String]; + CC_MD5_Update( &md5Ctx, data, strlen(data) ); +} + +-(void) addValue:(NSString*)value key:(NSString*) key +{ + + NSString *encodedValue = [self encodeData:value]; + NSString *encodedKey = [self encodeData:key]; + + [bodyValues addObject: [NSString stringWithFormat:@"%@=%@", encodedKey, encodedValue] ]; +} + +-(NSData*) getBodyValues { + NSMutableData *data = [[NSMutableData alloc] init]; + + BOOL first=YES; + for( NSString *s in bodyValues ) { + if( !first) + [data appendBytes:"&" length:1]; + + [data appendBytes:[s UTF8String] length:[s length]]; + first = NO; + } + + return [data autorelease]; +} + +-(NSString*) getHashForData +{ + NSString *ret; + unsigned char pTempKey[16]; + + // update the hash with the secret key + const char *data = [gameKey UTF8String]; + CC_MD5_Update(&md5Ctx, data, strlen(data)); + + // then get the hash + CC_MD5_Final( pTempKey, &md5Ctx); + +// NSData *nsdata = [NSData dataWithBytes:pTempKey length:16]; + ret = [NSString stringWithString:@""]; + for( int i=0;i<16;i++) { + ret = [NSString stringWithFormat:@"%@%02x", ret, pTempKey[i] ]; + } + + return ret; +} + +-(NSString*) encodeData:(NSString*) data +{ + NSString *newData; + + newData = [data stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + + // '&' and '=' should be encoded manually + newData = [newData stringByReplacingOccurrencesOfString:@"&" withString:@"%26"]; + newData = [newData stringByReplacingOccurrencesOfString:@"=" withString:@"%3D"]; + + return newData; +} + +#pragma mark NSURLConnection Delegate + +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response +{ + // this method is called when the server has determined that it + // has enough information to create the NSURLResponse + + // it can be called multiple times, for example in the case of a + // redirect, so each time we reset the data. + // receivedData is declared as a method instance elsewhere + [receivedData setLength:0]; +} + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + // append the new data to the receivedData + // receivedData is declared as a method instance elsewhere + [receivedData appendData:data]; + +// NSString *dataString = [NSString stringWithCString:[data bytes] length: [data length]]; +// CCLOG( @"data: %@", dataString); +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + CCLOG(@"Connection failed"); + + // wifi problems ? + postStatus_ = kPostStatusConnectionFailed; + + // release the connection + self.connection = nil; + + if( [delegate respondsToSelector:@selector(scorePostFail:) ] ) + [delegate scorePostFail:self]; +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + // release the connection + self.connection = nil; + +// NSString *dataString = [NSString stringWithCString:[receivedData bytes] length: [receivedData length]]; + NSString *dataString = [NSString stringWithCString:[receivedData bytes] encoding: NSASCIIStringEncoding]; + + if( [dataString hasPrefix:@"OK:"] ) { + // parse ranking and other possible answers + NSArray *values = [dataString componentsSeparatedByString:@":"]; + NSArray *answer = [ [values objectAtIndex:1] componentsSeparatedByString:@","]; + NSEnumerator *nse = [answer objectEnumerator]; + + // Create a holder for each line we are going to work with + NSString *line; + + // Loop through all the lines in the lines array processing each one + while( (line = [nse nextObject]) ) { + NSArray *keyvalue = [line componentsSeparatedByString:@"="]; +// NSLog(@"%@",keyvalue); + if( [[keyvalue objectAtIndex:0] isEqual:@"ranking"] ) { + ranking_ = [[keyvalue objectAtIndex:1] intValue]; + } else if( [[keyvalue objectAtIndex:0] isEqual:@"score_updated"] ) { + scoreDidUpdate_ = [[keyvalue objectAtIndex:1] boolValue]; + } + + } + if( [delegate respondsToSelector:@selector(scorePostOk:) ] ) + [delegate scorePostOk:self]; + + } else if( [dataString hasPrefix: @"OK"] ) { + + // Ok + postStatus_ = kPostStatusOK; + + if( [delegate respondsToSelector:@selector(scorePostOk:) ] ) + [delegate scorePostOk:self]; + + + } else { + + NSLog(@"cocoslive: Post Score failed. Reason: %@", dataString); + + // Error parsing answer + postStatus_ = kPostStatusPostFailed; + + if( [delegate respondsToSelector:@selector(scorePostFail:) ] ) + [delegate scorePostFail:self]; + } +} + +-(NSURLRequest *)connection:(NSURLConnection *)connection + willSendRequest:(NSURLRequest *)request + redirectResponse:(NSURLResponse *)redirectResponse +{ + NSURLRequest *newRequest=request; + if (redirectResponse) { + newRequest=nil; + } + + return newRequest; +} + +@end diff --git a/libs/cocoslive/CLScoreServerRequest.h b/libs/cocoslive/CLScoreServerRequest.h new file mode 100644 index 0000000..5428b23 --- /dev/null +++ b/libs/cocoslive/CLScoreServerRequest.h @@ -0,0 +1,121 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 + +// cocoslive definitions +#import "cocoslive.h" + +// Server URL +#if USE_LOCAL_SERVER +#define SCORE_SERVER_REQUEST_URL @"http://localhost:8080/api/get-scores" +#define SCORE_SERVER_GETRANK_URL @"http://localhost:8080/api/get-rank-for-score" +#else +#define SCORE_SERVER_REQUEST_URL @"http://www.cocoslive.net/api/get-scores" +#define SCORE_SERVER_GETRANK_URL @"http://www.cocoslive.net/api/get-rank-for-score" +#endif + +/** Type of predefined Query */ +typedef enum { + kQueryIgnore = 0, + kQueryDay = 1, + kQueryWeek = 2, + kQueryMonth = 3, + kQueryAllTime = 4, +} tQueryType; + +/** Flags that can be added to the query */ +typedef enum { + kQueryFlagIgnore = 0, + kQueryFlagByCountry = 1 << 0, + kQueryFlagByDevice = 1 << 1, +} tQueryFlags; + +/** + * Handles the Request Scores to the cocos live server + */ +@interface CLScoreServerRequest : NSObject { + + /// game name, used as a login name. + NSString *gameName; + + /// delegate instance of fetch score + id delegate; + + // data received + NSMutableData *receivedData; + + // To determine which delegate method will be called in connectionDidFinishLoading: of NSURLConnection Delegate + BOOL reqRankOnly; + + /// the connection + NSURLConnection *connection_; +} + +/** connection to the server */ +@property (nonatomic, retain) NSURLConnection *connection; + + +/** creates a ScoreServerRequest server with a game name*/ ++(id) serverWithGameName:(NSString*) name delegate:(id)delegate; + +/** initializes a ScoreServerRequest with a game name*/ +-(id) initWithGameName:(NSString*) name delegate:(id)delegate; + +/** request scores from server using a predefined query. This is an asyncronous request. + * limit: how many scores are being requested. Maximun is 100 + * flags: can be kQueryFlagByCountry (fetches only scores from country) + * category: an NSString. For example: 'easy', 'medium', 'type1'... When requesting scores, they can be filtered by this field. + */ +-(BOOL) requestScores: (tQueryType) type limit:(int)limit offset:(int)offset flags:(tQueryFlags)flags category:(NSString*)category; + +/** request scores from server using a predefined query. This is an asyncronous request. + * limit: how many scores are being requested. Maximun is 100 + * flags: can be kQueryFlagByCountry (fetches only scores from country) + */ +-(BOOL) requestScores: (tQueryType) type limit:(int)limit offset:(int)offset flags:(tQueryFlags)flags; + +/** parse the received JSON scores and convert it to objective-c objects */ +-(NSArray*) parseScores; + +/** request rank for a given score using a predefined query. This is an asyncronous request. + * score: int for a score + * category: an NSString. For example: 'easy', 'medium', 'type1'... When requesting ranks, they can be filtered by this field. + */ +-(BOOL) requestRankForScore:(int)score andCategory:(NSString*)category; + +/** It's actually not parsing anything, just returning int for a rank. Kept name PARSE for convinience with parseScores */ +-(int) parseRank; + +@end + +/** CocosLive Request protocol */ +@protocol CLRequestDelegate +-(void) scoreRequestOk:(id) sender; +-(void) scoreRequestRankOk:(id) sender; +-(void) scoreRequestFail:(id) sender; +@end diff --git a/libs/cocoslive/CLScoreServerRequest.m b/libs/cocoslive/CLScoreServerRequest.m new file mode 100644 index 0000000..2e6bc76 --- /dev/null +++ b/libs/cocoslive/CLScoreServerRequest.m @@ -0,0 +1,256 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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. + * + */ + + +// 3rd party imports +#import "CJSONDeserializer.h" + +// local imports +#import "CLScoreServerPost.h" +#import "CLScoreServerRequest.h" +#import "ccMacros.h" + +@implementation CLScoreServerRequest + +@synthesize connection=connection_; + ++(id) serverWithGameName:(NSString*) name delegate:(id)delegate +{ + return [[[self alloc] initWithGameName:name delegate:delegate] autorelease]; +} + +-(id) initWithGameName:(NSString*) name delegate:(id)aDelegate +{ + self = [super init]; + if( self ) { + gameName = [[name stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] retain]; + delegate = [aDelegate retain]; + receivedData = [[NSMutableData data] retain]; + } + return self; +} + +-(void) dealloc +{ + CCLOGINFO(@"deallocing %@", self); + + [delegate release]; + [gameName release]; + [receivedData release]; + [connection_ release]; + [super dealloc]; +} + +-(BOOL) requestScores:(tQueryType)type + limit:(int)limit + offset:(int)offset + flags:(tQueryFlags)flags + category:(NSString*)category +{ + // create the request + [receivedData setLength:0]; + + // it's not a call for rank + reqRankOnly = NO; + + NSString *device = @""; + if( flags & kQueryFlagByDevice ) + device = [[UIDevice currentDevice] uniqueIdentifier]; + + // arguments: + // query: type of query + // limit: how many scores are being requested. Default is 25. Maximun is 100 + // offset: offset of the scores + // flags: bring only country scores, world scores, etc. + // category: string user defined string used to filter + NSString *url= [NSString stringWithFormat:@"%@?gamename=%@&querytype=%d&offset=%d&limit=%d&flags=%d&category=%@&device=%@", + SCORE_SERVER_REQUEST_URL, + gameName, + type, + offset, + limit, + flags, + [category stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding], + device + ]; + + // NSLog(@"%@", url); + + NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:url] + cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:10.0]; + + // create the connection with the request + // and start loading the data + self.connection=[[NSURLConnection alloc] initWithRequest:request delegate:self]; + if (! connection_) + return NO; + + return YES; +} + +-(BOOL) requestScores:(tQueryType)type + limit:(int)limit + offset:(int)offset + flags:(tQueryFlags)flags +{ + // create the request + [receivedData setLength:0]; + + // arguments: + // query: type of query + // limit: how many scores are being requested. Maximun is 100 + // offset: offset of the scores + // flags: bring only country scores, world scores, etc. + return [self requestScores:type limit:limit offset:offset flags:flags category:@""]; +} + +-(NSArray*) parseScores +{ + NSArray *array = nil; + NSError *error = nil; + NSDictionary *dictionary = [[CJSONDeserializer deserializer] deserializeAsDictionary:receivedData error:&error]; + +// NSLog(@"r: %@", dictionary); + if( ! error ) { + array = [dictionary objectForKey:@"scores"]; + } else { + CCLOG(@"Error parsing scores: %@", error); + } + return array; +} + +#pragma mark Request rank for score + +-(BOOL) requestRankForScore:(int)score andCategory:(NSString*)category { + // create the request + [receivedData setLength:0]; + + reqRankOnly = YES; + + // arguments: + // score: score for which you need rank + // category: user defined string used to filter + NSString *url= [NSString stringWithFormat:@"%@?gamename=%@&category=%@&score=%d", + SCORE_SERVER_GETRANK_URL, + gameName, + [category stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding], + score + ]; + + NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:url] + cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:10.0]; + + // create the connection with the request + // and start loading the data + self.connection=[[NSURLConnection alloc] initWithRequest:request delegate:self]; + if (! connection_) + return NO; + + return YES; +} + +-(int) parseRank { +// NSString *rankStr = [NSString stringWithCString:[receivedData bytes] length: [receivedData length]]; + NSString *rankStr = [NSString stringWithCString:[receivedData bytes] encoding: NSASCIIStringEncoding]; + +// NSLog(@"XXXX: Ranking: %@", rankStr); + + // creating trimmed string by trimming everything that's not numbers from the receivedData + NSString *trimmedStr = [rankStr stringByTrimmingCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]]; + + int scoreInt = [trimmedStr intValue]; + + return scoreInt; +} + + +#pragma mark NSURLConnection Delegate + +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response +{ + // this method is called when the server has determined that it + // has enough information to create the NSURLResponse + + // it can be called multiple times, for example in the case of a + // redirect, so each time we reset the data. + // receivedData is declared as a method instance elsewhere + + [receivedData setLength:0]; +} + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + // append the new data to the receivedData + // receivedData is declared as a method instance elsewhere + [receivedData appendData:data]; +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + // release the connection, and the data object + self.connection = nil; + + + CCLOG(@"Error getting scores: %@", error); + + if( [delegate respondsToSelector:@selector(scoreRequestFail:) ] ) + [delegate scoreRequestFail:self]; + +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + // release the connection, and the data object + self.connection = nil; + + + if(reqRankOnly) { + // because it's request for rank, different delegate method is called scoreRequestRankOk: + // if connection failed the same delegate method is used as for standard scores - scoreRequestFail: + if( [delegate respondsToSelector:@selector(scoreRequestRankOk:) ] ) { + [delegate scoreRequestRankOk:self]; + } + } else { + if( [delegate respondsToSelector:@selector(scoreRequestOk:) ] ) { + [delegate scoreRequestOk:self]; + } + + } +} + +-(NSURLRequest *)connection:(NSURLConnection *)connection + willSendRequest:(NSURLRequest *)request + redirectResponse:(NSURLResponse *)redirectResponse +{ + NSURLRequest *newRequest=request; + if (redirectResponse) { + newRequest=nil; + } + return newRequest; +} + +@end diff --git a/libs/cocoslive/cocoslive.h b/libs/cocoslive/cocoslive.h new file mode 100644 index 0000000..2c7dfe4 --- /dev/null +++ b/libs/cocoslive/cocoslive.h @@ -0,0 +1,42 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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. + * + */ + + + +// 0x00 HI ME LO +// 00 00 03 02 +#define COCOSLIVE_VERSION 0x00000302 + +// to use localserver. DEBUG ONLY +//#define USE_LOCAL_SERVER 1 + +// all cocos live include files +// +#import "CLScoreServerPost.h" +#import "CLScoreServerRequest.h" + + +// free functions +NSString * cocos2dVersion(void); diff --git a/libs/cocoslive/cocoslive.m b/libs/cocoslive/cocoslive.m new file mode 100644 index 0000000..2883659 --- /dev/null +++ b/libs/cocoslive/cocoslive.m @@ -0,0 +1,34 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * + * 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 + +static NSString *version = @"cocoslive v0.3.2"; + +NSString *cocosLiveVersion() +{ + return version; +} diff --git a/main.m b/main.m new file mode 100755 index 0000000..505e81b --- /dev/null +++ b/main.m @@ -0,0 +1,16 @@ +// +// main.m +// CCMenuAdvanced +// +// Created by Stepan Generalov on 14.04.11. +// Copyright __MyCompanyName__ 2011. All rights reserved. +// + +#import + +int main(int argc, char *argv[]) { + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + int retVal = UIApplicationMain(argc, argv, nil, @"CCMenuAdvancedAppDelegate"); + [pool release]; + return retVal; +}