From beaeeb2420bee002294e55a360e273bc2928be69 Mon Sep 17 00:00:00 2001 From: Josh Aburto Date: Tue, 29 Sep 2009 23:02:50 -0700 Subject: [PATCH] Initial commit --- .gitattributes | 1 + .gitignore | 11 + Classes/Block.h | 35 + Classes/Block.m | 111 ++ Classes/GameLogicLayer.h | 38 + Classes/GameLogicLayer.m | 271 ++++ Classes/GameScene.h | 17 + Classes/GameScene.m | 37 + Classes/TetrisAppDelegate.h | 17 + Classes/TetrisAppDelegate.m | 76 ++ Classes/Tetromino.h | 40 + Classes/Tetromino.m | 169 +++ CocosDenshion/CDAudioManager.h | 125 ++ CocosDenshion/CDAudioManager.m | 492 +++++++ CocosDenshion/CDOpenALSupport.h | 253 ++++ CocosDenshion/CocosDenshion.h | 242 ++++ CocosDenshion/CocosDenshion.m | 755 +++++++++++ CocosDenshion/SimpleAudioEngine.h | 67 + CocosDenshion/SimpleAudioEngine.m | 247 ++++ Default.png | Bin 0 -> 30635 bytes Icon.png | Bin 0 -> 9327 bytes Info.plist | 26 + Tetris.xcodeproj/project.pbxproj | 1119 ++++++++++++++++ Tetris_Prefix.pch | 8 + TouchJSON/CDataScanner.h | 68 + TouchJSON/CDataScanner.m | 270 ++++ .../Extensions/CDataScanner_Extensions.h | 37 + .../Extensions/CDataScanner_Extensions.m | 80 ++ .../Extensions/NSCharacterSet_Extensions.h | 36 + .../Extensions/NSCharacterSet_Extensions.m | 48 + .../Extensions/NSDictionary_JSONExtensions.h | 36 + .../Extensions/NSDictionary_JSONExtensions.m | 41 + TouchJSON/Extensions/NSScanner_Extensions.h | 44 + TouchJSON/Extensions/NSScanner_Extensions.m | 118 ++ TouchJSON/JSON/CJSONDeserializer.h | 59 + TouchJSON/JSON/CJSONDeserializer.m | 84 ++ TouchJSON/JSON/CJSONScanner.h | 43 + TouchJSON/JSON/CJSONScanner.m | 536 ++++++++ TouchJSON/JSON/CJSONSerializer.h | 45 + TouchJSON/JSON/CJSONSerializer.m | 184 +++ background.jpg | Bin 0 -> 6141 bytes blue.png | Bin 0 -> 398 bytes cocos2d/Action.h | 109 ++ cocos2d/Action.m | 232 ++++ cocos2d/ActionManager.h | 92 ++ cocos2d/ActionManager.m | 356 ++++++ cocos2d/AtlasNode.h | 78 ++ cocos2d/AtlasNode.m | 173 +++ cocos2d/AtlasSprite.h | 149 +++ cocos2d/AtlasSprite.m | 525 ++++++++ cocos2d/AtlasSpriteManager.h | 79 ++ cocos2d/AtlasSpriteManager.m | 339 +++++ cocos2d/BitmapFontAtlas.h | 125 ++ cocos2d/BitmapFontAtlas.m | 461 +++++++ cocos2d/Camera.h | 70 + cocos2d/Camera.m | 157 +++ cocos2d/CameraAction.h | 57 + cocos2d/CameraAction.m | 127 ++ cocos2d/CocosNode.h | 489 +++++++ cocos2d/CocosNode.m | 726 +++++++++++ cocos2d/Director.h | 282 ++++ cocos2d/Director.m | 911 +++++++++++++ cocos2d/DrawingPrimitives.h | 71 ++ cocos2d/DrawingPrimitives.m | 150 +++ cocos2d/EaseAction.h | 72 ++ cocos2d/EaseAction.m | 230 ++++ cocos2d/Grabber.h | 32 + cocos2d/Grabber.m | 74 ++ cocos2d/Grid.h | 114 ++ cocos2d/Grid.m | 480 +++++++ cocos2d/Grid3DAction.h | 193 +++ cocos2d/Grid3DAction.m | 522 ++++++++ cocos2d/GridAction.h | 157 +++ cocos2d/GridAction.m | 348 +++++ cocos2d/InstantAction.h | 99 ++ cocos2d/InstantAction.m | 223 ++++ cocos2d/IntervalAction.h | 356 ++++++ cocos2d/IntervalAction.m | 1131 +++++++++++++++++ cocos2d/Label.h | 50 + cocos2d/Label.m | 86 ++ cocos2d/LabelAtlas.h | 43 + cocos2d/LabelAtlas.m | 136 ++ cocos2d/Layer.h | 120 ++ cocos2d/Layer.m | 325 +++++ cocos2d/Menu.h | 74 ++ cocos2d/Menu.m | 456 +++++++ cocos2d/MenuItem.h | 249 ++++ cocos2d/MenuItem.m | 676 ++++++++++ cocos2d/MotionStreak.h | 56 + cocos2d/MotionStreak.m | 92 ++ cocos2d/ParallaxNode.h | 37 + cocos2d/ParallaxNode.m | 153 +++ cocos2d/ParticleExamples.h | 82 ++ cocos2d/ParticleExamples.m | 883 +++++++++++++ cocos2d/ParticleSystem.h | 265 ++++ cocos2d/ParticleSystem.m | 234 ++++ cocos2d/PointParticleSystem.h | 34 + cocos2d/PointParticleSystem.m | 257 ++++ cocos2d/QuadParticleSystem.h | 44 + cocos2d/QuadParticleSystem.m | 319 +++++ cocos2d/RenderTexture.h | 64 + cocos2d/RenderTexture.m | 189 +++ cocos2d/Ribbon.h | 105 ++ cocos2d/Ribbon.m | 365 ++++++ cocos2d/Scene.h | 33 + cocos2d/Scene.m | 32 + cocos2d/Scheduler.h | 106 ++ cocos2d/Scheduler.m | 217 ++++ cocos2d/Sprite.h | 100 ++ cocos2d/Sprite.m | 253 ++++ cocos2d/Support/CGPointExtension.h | 222 ++++ cocos2d/Support/CGPointExtension.m | 55 + cocos2d/Support/EAGLView.h | 145 +++ cocos2d/Support/EAGLView.m | 316 +++++ cocos2d/Support/FileUtils.h | 24 + cocos2d/Support/FileUtils.m | 41 + cocos2d/Support/OpenGL_Internal.h | 79 ++ cocos2d/Support/PVRTexture.h | 81 ++ cocos2d/Support/PVRTexture.m | 280 ++++ cocos2d/Support/TGAlib.h | 55 + cocos2d/Support/TGAlib.m | 272 ++++ cocos2d/Support/Texture2D.h | 224 ++++ cocos2d/Support/Texture2D.m | 525 ++++++++ cocos2d/Support/TransformUtils.h | 19 + cocos2d/Support/TransformUtils.m | 34 + cocos2d/Support/ZipUtils.h | 41 + cocos2d/Support/ZipUtils.m | 125 ++ cocos2d/Support/base64.c | 90 ++ cocos2d/Support/base64.h | 34 + cocos2d/Support/ccArray.h | 212 +++ cocos2d/Support/ccHashSet.h | 127 ++ cocos2d/Support/ccHashSet.m | 296 +++++ cocos2d/Support/glu.c | 112 ++ cocos2d/Support/glu.h | 21 + cocos2d/TMXTiledMap.h | 166 +++ cocos2d/TMXTiledMap.m | 310 +++++ cocos2d/TMXXMLParser.h | 123 ++ cocos2d/TMXXMLParser.m | 244 ++++ cocos2d/TextureAtlas.h | 117 ++ cocos2d/TextureAtlas.m | 272 ++++ cocos2d/TextureMgr.h | 99 ++ cocos2d/TextureMgr.m | 292 +++++ cocos2d/TextureNode.h | 57 + cocos2d/TextureNode.m | 117 ++ cocos2d/TileMapAtlas.h | 70 + cocos2d/TileMapAtlas.m | 204 +++ cocos2d/TiledGridAction.h | 198 +++ cocos2d/TiledGridAction.m | 663 ++++++++++ cocos2d/TouchDelegateProtocol.h | 65 + cocos2d/TouchDispatcher.h | 56 + cocos2d/TouchDispatcher.m | 213 ++++ cocos2d/TouchHandler.h | 70 + cocos2d/TouchHandler.m | 181 +++ cocos2d/Transition.h | 262 ++++ cocos2d/Transition.m | 908 +++++++++++++ cocos2d/ccExceptions.h | 23 + cocos2d/ccMacros.h | 55 + cocos2d/ccTypes.h | 217 ++++ cocos2d/cocos2d.h | 99 ++ cocos2d/cocos2d.m | 22 + cocoslive/ScoreServerPost.h | 123 ++ cocoslive/ScoreServerPost.m | 318 +++++ cocoslive/ScoreServerRequest.h | 103 ++ cocoslive/ScoreServerRequest.m | 245 ++++ cocoslive/cocoslive.h | 30 + cocoslive/cocoslive.m | 22 + cyan.png | Bin 0 -> 419 bytes fps_images.png | Bin 0 -> 6203 bytes gameover.jpeg | Bin 0 -> 9842 bytes green.png | Bin 0 -> 404 bytes magenta.png | Bin 0 -> 437 bytes main.m | 16 + orange.png | Bin 0 -> 402 bytes red.png | Bin 0 -> 388 bytes yellow.png | Bin 0 -> 388 bytes 175 files changed, 31200 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Classes/Block.h create mode 100644 Classes/Block.m create mode 100644 Classes/GameLogicLayer.h create mode 100644 Classes/GameLogicLayer.m create mode 100644 Classes/GameScene.h create mode 100644 Classes/GameScene.m create mode 100755 Classes/TetrisAppDelegate.h create mode 100755 Classes/TetrisAppDelegate.m create mode 100644 Classes/Tetromino.h create mode 100644 Classes/Tetromino.m create mode 100644 CocosDenshion/CDAudioManager.h create mode 100644 CocosDenshion/CDAudioManager.m create mode 100644 CocosDenshion/CDOpenALSupport.h create mode 100644 CocosDenshion/CocosDenshion.h create mode 100644 CocosDenshion/CocosDenshion.m create mode 100644 CocosDenshion/SimpleAudioEngine.h create mode 100644 CocosDenshion/SimpleAudioEngine.m create mode 100755 Default.png create mode 100755 Icon.png create mode 100755 Info.plist create mode 100755 Tetris.xcodeproj/project.pbxproj create mode 100755 Tetris_Prefix.pch create mode 100644 TouchJSON/CDataScanner.h create mode 100644 TouchJSON/CDataScanner.m create mode 100644 TouchJSON/Extensions/CDataScanner_Extensions.h create mode 100644 TouchJSON/Extensions/CDataScanner_Extensions.m create mode 100644 TouchJSON/Extensions/NSCharacterSet_Extensions.h create mode 100644 TouchJSON/Extensions/NSCharacterSet_Extensions.m create mode 100644 TouchJSON/Extensions/NSDictionary_JSONExtensions.h create mode 100644 TouchJSON/Extensions/NSDictionary_JSONExtensions.m create mode 100644 TouchJSON/Extensions/NSScanner_Extensions.h create mode 100644 TouchJSON/Extensions/NSScanner_Extensions.m create mode 100755 TouchJSON/JSON/CJSONDeserializer.h create mode 100755 TouchJSON/JSON/CJSONDeserializer.m create mode 100644 TouchJSON/JSON/CJSONScanner.h create mode 100644 TouchJSON/JSON/CJSONScanner.m create mode 100644 TouchJSON/JSON/CJSONSerializer.h create mode 100644 TouchJSON/JSON/CJSONSerializer.m create mode 100644 background.jpg create mode 100644 blue.png create mode 100644 cocos2d/Action.h create mode 100644 cocos2d/Action.m create mode 100644 cocos2d/ActionManager.h create mode 100644 cocos2d/ActionManager.m create mode 100644 cocos2d/AtlasNode.h create mode 100644 cocos2d/AtlasNode.m create mode 100644 cocos2d/AtlasSprite.h create mode 100644 cocos2d/AtlasSprite.m create mode 100644 cocos2d/AtlasSpriteManager.h create mode 100644 cocos2d/AtlasSpriteManager.m create mode 100644 cocos2d/BitmapFontAtlas.h create mode 100644 cocos2d/BitmapFontAtlas.m create mode 100644 cocos2d/Camera.h create mode 100644 cocos2d/Camera.m create mode 100644 cocos2d/CameraAction.h create mode 100644 cocos2d/CameraAction.m create mode 100644 cocos2d/CocosNode.h create mode 100644 cocos2d/CocosNode.m create mode 100644 cocos2d/Director.h create mode 100644 cocos2d/Director.m create mode 100644 cocos2d/DrawingPrimitives.h create mode 100644 cocos2d/DrawingPrimitives.m create mode 100644 cocos2d/EaseAction.h create mode 100644 cocos2d/EaseAction.m create mode 100644 cocos2d/Grabber.h create mode 100644 cocos2d/Grabber.m create mode 100644 cocos2d/Grid.h create mode 100644 cocos2d/Grid.m create mode 100644 cocos2d/Grid3DAction.h create mode 100644 cocos2d/Grid3DAction.m create mode 100644 cocos2d/GridAction.h create mode 100644 cocos2d/GridAction.m create mode 100644 cocos2d/InstantAction.h create mode 100644 cocos2d/InstantAction.m create mode 100644 cocos2d/IntervalAction.h create mode 100644 cocos2d/IntervalAction.m create mode 100644 cocos2d/Label.h create mode 100644 cocos2d/Label.m create mode 100644 cocos2d/LabelAtlas.h create mode 100644 cocos2d/LabelAtlas.m create mode 100644 cocos2d/Layer.h create mode 100644 cocos2d/Layer.m create mode 100644 cocos2d/Menu.h create mode 100644 cocos2d/Menu.m create mode 100644 cocos2d/MenuItem.h create mode 100644 cocos2d/MenuItem.m create mode 100644 cocos2d/MotionStreak.h create mode 100644 cocos2d/MotionStreak.m create mode 100644 cocos2d/ParallaxNode.h create mode 100644 cocos2d/ParallaxNode.m create mode 100644 cocos2d/ParticleExamples.h create mode 100644 cocos2d/ParticleExamples.m create mode 100644 cocos2d/ParticleSystem.h create mode 100644 cocos2d/ParticleSystem.m create mode 100644 cocos2d/PointParticleSystem.h create mode 100644 cocos2d/PointParticleSystem.m create mode 100644 cocos2d/QuadParticleSystem.h create mode 100644 cocos2d/QuadParticleSystem.m create mode 100644 cocos2d/RenderTexture.h create mode 100644 cocos2d/RenderTexture.m create mode 100644 cocos2d/Ribbon.h create mode 100644 cocos2d/Ribbon.m create mode 100644 cocos2d/Scene.h create mode 100644 cocos2d/Scene.m create mode 100644 cocos2d/Scheduler.h create mode 100644 cocos2d/Scheduler.m create mode 100644 cocos2d/Sprite.h create mode 100644 cocos2d/Sprite.m create mode 100644 cocos2d/Support/CGPointExtension.h create mode 100644 cocos2d/Support/CGPointExtension.m create mode 100755 cocos2d/Support/EAGLView.h create mode 100755 cocos2d/Support/EAGLView.m create mode 100644 cocos2d/Support/FileUtils.h create mode 100644 cocos2d/Support/FileUtils.m create mode 100755 cocos2d/Support/OpenGL_Internal.h create mode 100755 cocos2d/Support/PVRTexture.h create mode 100755 cocos2d/Support/PVRTexture.m create mode 100644 cocos2d/Support/TGAlib.h create mode 100644 cocos2d/Support/TGAlib.m create mode 100755 cocos2d/Support/Texture2D.h create mode 100755 cocos2d/Support/Texture2D.m create mode 100644 cocos2d/Support/TransformUtils.h create mode 100644 cocos2d/Support/TransformUtils.m create mode 100644 cocos2d/Support/ZipUtils.h create mode 100644 cocos2d/Support/ZipUtils.m create mode 100644 cocos2d/Support/base64.c create mode 100644 cocos2d/Support/base64.h create mode 100644 cocos2d/Support/ccArray.h create mode 100644 cocos2d/Support/ccHashSet.h create mode 100644 cocos2d/Support/ccHashSet.m create mode 100755 cocos2d/Support/glu.c create mode 100755 cocos2d/Support/glu.h create mode 100644 cocos2d/TMXTiledMap.h create mode 100644 cocos2d/TMXTiledMap.m create mode 100644 cocos2d/TMXXMLParser.h create mode 100644 cocos2d/TMXXMLParser.m create mode 100644 cocos2d/TextureAtlas.h create mode 100644 cocos2d/TextureAtlas.m create mode 100644 cocos2d/TextureMgr.h create mode 100644 cocos2d/TextureMgr.m create mode 100644 cocos2d/TextureNode.h create mode 100644 cocos2d/TextureNode.m create mode 100644 cocos2d/TileMapAtlas.h create mode 100644 cocos2d/TileMapAtlas.m create mode 100644 cocos2d/TiledGridAction.h create mode 100644 cocos2d/TiledGridAction.m create mode 100644 cocos2d/TouchDelegateProtocol.h create mode 100644 cocos2d/TouchDispatcher.h create mode 100644 cocos2d/TouchDispatcher.m create mode 100644 cocos2d/TouchHandler.h create mode 100644 cocos2d/TouchHandler.m create mode 100644 cocos2d/Transition.h create mode 100644 cocos2d/Transition.m create mode 100644 cocos2d/ccExceptions.h create mode 100644 cocos2d/ccMacros.h create mode 100644 cocos2d/ccTypes.h create mode 100644 cocos2d/cocos2d.h create mode 100644 cocos2d/cocos2d.m create mode 100644 cocoslive/ScoreServerPost.h create mode 100644 cocoslive/ScoreServerPost.m create mode 100644 cocoslive/ScoreServerRequest.h create mode 100644 cocoslive/ScoreServerRequest.m create mode 100644 cocoslive/cocoslive.h create mode 100644 cocoslive/cocoslive.m create mode 100644 cyan.png create mode 100644 fps_images.png create mode 100644 gameover.jpeg create mode 100644 green.png create mode 100644 magenta.png create mode 100755 main.m create mode 100644 orange.png create mode 100644 red.png create mode 100644 yellow.png diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..0ef1ffb --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.pbxproj -crlf -diff -merge \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..87cca62 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# xcode noise +build/* +*.pbxuser +*.mode1v3 + +# old skool +.svn + +# osx noise +.DS_Store +profile \ No newline at end of file diff --git a/Classes/Block.h b/Classes/Block.h new file mode 100644 index 0000000..a1a761d --- /dev/null +++ b/Classes/Block.h @@ -0,0 +1,35 @@ +// +// Tetromino.h +// Tetris +// +// Created by Joshua Aburto on 9/26/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import "cocos2d.h" + + +@interface Block : Sprite { + int boardX, boardY; + BOOL stuck; + BOOL disappearing; + + +} + +@property int boardX; +@property int boardY; +@property BOOL stuck; +@property BOOL disappearing; + ++ (Block *)newBlock:(int)blockType; +- (void)moveUp; +- (void)moveDown; +- (void)moveLeft; +- (void)moveRight; + +@end + +#define COMPUTE_X(x) (abs(x) * 24) +#define COMPUTE_Y(y) (456 - (abs(y) * 24)) +#define COMPUTE_X_Y(x,y) ccp( COMPUTE_X(x), COMPUTE_Y(y)) \ No newline at end of file diff --git a/Classes/Block.m b/Classes/Block.m new file mode 100644 index 0000000..2806f21 --- /dev/null +++ b/Classes/Block.m @@ -0,0 +1,111 @@ +// +// Tetromino.m +// Tetris +// +// Created by Joshua Aburto on 9/26/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import "Block.h" + +@interface Block (private) + +- (void)initializeDefaults; +- (void)redrawPositionOnBoard; + +@end + +@implementation Block + +@synthesize boardX; +@synthesize boardY; +@synthesize stuck; +@synthesize disappearing; + ++ (Block *)newBlock:(int)blockType +{ + NSString *filename = nil, *color = nil; + Block *temp = nil; + + switch (blockType) { + case 0: + color = @"red"; + break; + case 1: + color = @"blue"; + break; + case 2: + color = @"orange"; + break; + case 3: + color = @"yellow"; + break; + case 4: + color = @"magenta"; + break; + case 5: + color = @"cyan"; + break; + case 6: + color = @"green"; + break; + default: + break; + } + + if (color) { + filename = [[NSString alloc] initWithFormat:@"%@.png", color]; + temp = [[self spriteWithFile:filename] retain]; + [filename release]; + + [temp initializeDefaults]; + + } + return temp; + +} + + +- (void)initializeDefaults +{ + [self setAnchorPoint: ccp(0,0)]; + [self setPosition: ccp(0,0)]; + [self setOpacity:255]; + [self setStuck:NO]; + [self setDisappearing:NO]; + [self setBoardX:0]; + [self setBoardY:0]; +} + +- (void)redrawPositionOnBoard +{ + [self setPosition: COMPUTE_X_Y(boardX, boardY)]; +} + +- (void)moveUp +{ + boardY -= 1; + [self redrawPositionOnBoard]; +} + +- (void)moveDown +{ + boardY += 1; + [self redrawPositionOnBoard]; +} + +- (void)moveLeft +{ + boardX -= 1; + [self redrawPositionOnBoard]; +} + +- (void)moveRight +{ + boardX += 1; + [self redrawPositionOnBoard]; +} + + + +@end diff --git a/Classes/GameLogicLayer.h b/Classes/GameLogicLayer.h new file mode 100644 index 0000000..9d8152f --- /dev/null +++ b/Classes/GameLogicLayer.h @@ -0,0 +1,38 @@ +// +// GameLogicLayer.h +// Tetris +// +// Created by Joshua Aburto on 9/26/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import "Tetromino.h" +#import "Block.h" +#define kLastColumn 9 +#define kLastRow 19 + +@interface GameLogicLayer : Layer { + enum touchTypes { + kNone, + kDropBlocks, + kBlockFlip, + kMoveLeft, + kMoveRight + } touchType; + + Block *board[kLastColumn + 1][kLastRow + 1]; + Tetromino *userTetromino; + int frameCount; + int moveCycleRatio; + int difficulty; + int score; + Label *scoreLabel; + Label *difficultyLabel; + + +} + + +- (void)updateBoard:(ccTime)dt; + +@end diff --git a/Classes/GameLogicLayer.m b/Classes/GameLogicLayer.m new file mode 100644 index 0000000..4e7f53a --- /dev/null +++ b/Classes/GameLogicLayer.m @@ -0,0 +1,271 @@ +// +// GameLogicLayer.m +// Tetris +// +// Created by Joshua Aburto on 9/26/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import "GameLogicLayer.h" + +@interface GameLogicLayer (private) + +- (void)startGame; +- (void)createNewTetromino; +- (void)tryToCreateNewTetromino; + +- (void)moveBlocksDown; +- (void)moveBlockDown:(Block *)block; +- (void)moveBlockLeft:(Block *)block; +- (void)moveBlockRight:(Block *)block; + +- (void)gameOver; +- (BOOL)isBlockInTetromino:(Block *)block; +- (void)moveTetrominoDown; + +- (BOOL)boardRowEmpty:(int)column; + +- (void)moveTetrominoLeft; +- (void)moveTetrominoRight; + +@end + +@implementation GameLogicLayer + +- (id)init +{ + if ((self = [super init])) { + isTouchEnabled = YES; + [self startGame]; + } + return self; +} + +- (void)startGame +{ + memset(board, 0, sizeof(board)); + + [self createNewTetromino]; + frameCount = 0; + moveCycleRatio = 45; + [self schedule:@selector(updateBoard:) interval:(1.0/60.0)]; +} + +- (void)createNewTetromino +{ + + Tetromino *tempTetromino = [[Tetromino alloc] init]; + + for (Block *currentBlock in tempTetromino.blockArray) { + NSLog(@"%@", currentBlock); + board[currentBlock.boardX][currentBlock.boardY] = currentBlock; + } + userTetromino = tempTetromino; + [self addChild:userTetromino]; + [tempTetromino release]; + +} +- (void)tryToCreateNewTetromino +{ + // If any spot in the top two rows where blocks spawn is taken + for (int i = 4; i < 8; i++) { + for (int j = 0; j < 2; j++) { + if (board[i][j]) { + [self gameOver]; + } + + } + } + [self createNewTetromino]; +} +- (void)gameOver +{ + [self unschedule:@selector(updateFrame:)]; + + Sprite *gameOverBackground = [Sprite spriteWithFile:@"gameover.jpeg"]; + gameOverBackground.position = ccp(160,240); + gameOverBackground.opacity = 255; + [self addChild:gameOverBackground z:3]; + +} +- (void)moveBlocksDown +{ + Block *currentBlock = nil; + BOOL alreadyMovedTetromino = NO; + // Get a new Tetromino if Tetromino cant move anymore + if(userTetromino.stuck) { + [self tryToCreateNewTetromino]; + } + + // Go through board from bottom to top + + + for (int x = kLastColumn; x >= 0 ; x--) { + for (int y = kLastRow; y >= 0; y--) { + currentBlock = board[x][y]; + if (currentBlock != nil) { + if ([self isBlockInTetromino:currentBlock]) { + if (!(alreadyMovedTetromino)) { + [self moveTetrominoDown]; + alreadyMovedTetromino = YES; + } + + + } else if (y != kLastRow && ([self boardRowEmpty:x])){ + [self moveBlockDown:currentBlock]; + currentBlock.stuck = NO; + } + else { + currentBlock.stuck = YES; + } + } + } + } + +} + +- (BOOL)boardRowEmpty:(int)column +{ + for (int i = 0;i < kLastRow; i++) { + if (board[column][i]) { + return NO; + } + } + return YES; +} + +- (void)moveTetrominoDown +{ + + + + + for (Block *currentBlock in userTetromino.blockArray) { + Block *blockUnderCurrentBlock = board[currentBlock.boardX][currentBlock.boardY + 1]; + if (!([self isBlockInTetromino:blockUnderCurrentBlock])) { + + if (currentBlock.boardY == kLastRow || + (board[currentBlock.boardX][currentBlock.boardY + 1] != nil)) { + userTetromino.stuck = YES; + } + } + } + + if (!userTetromino.stuck) { + for (Block *block in userTetromino.blockArray) { + [self moveBlockDown:block]; + } + } +} + +- (void)moveTetrominoLeft +{ + for (Block *currentBlock in userTetromino.blockArray) { + [self moveBlockLeft:currentBlock]; + } +} + +- (void)moveTetrominoRight +{ + for (Block *currentBlock in userTetromino.blockArray) { + [self moveBlockRight:currentBlock]; + } +} + +- (BOOL)isBlockInTetromino:(id)block +{ + for (Block *currentBlock in userTetromino.blockArray) { + if ([currentBlock isEqual:block]) { + return YES; + } + + } + return NO; +} + +- (BOOL)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + UITouch *touch = [touches anyObject]; + CGPoint point = [touch locationInView: [touch view]]; + + //Flip the UITouch coord upside down for portrait mode + // Because UITouch goes from top down and Cocos2d goes from bottom up? + point.y = 480 - point.y; + + if (point.x < 116) { + touchType = kMoveLeft; + } else if (point.x > 204) { + touchType = kMoveRight; + } + + return kEventHandled; +} + +- (void)processTaps +{ + NSLog(@"Processing Taps"); + if (touchType == kMoveLeft) { + touchType = kNone; + + if (userTetromino.leftMostPosition.x > 0 && !userTetromino.stuck) { + [self moveTetrominoLeft]; + } + } else if (touchType == kMoveRight) { + touchType = kNone; + + if (userTetromino.rightMostPosition.x < kLastColumn && !userTetromino.stuck) { + [self moveTetrominoRight]; + } + } + +} + + +- (void)moveBlockDown:(Block *)block +{ + board[block.boardX][block.boardY] = nil; + board[block.boardX][block.boardY + 1] = block; + [block moveDown]; +} + +- (void)moveBlockLeft:(Block *)block +{ + if (nil == board[block.boardX - 1][block.boardY]) { + board[block.boardX][block.boardY] = nil; + board[block.boardX - 1][block.boardY] = block; + block.moveLeft; + } + +} + +- (void)moveBlockRight:(Block *)block +{ + if (nil == board[block.boardX + 1][block.boardY]) { + board[block.boardX][block.boardY] = nil; + board[block.boardX + 1][block.boardY] = block; + block.moveRight; + } + +} + +- (void)updateBoard:(ccTime)dt +{ + frameCount += 1; + [self processTaps]; + if (frameCount % moveCycleRatio == 0) { + [self moveBlocksDown]; + + } + + +} + +- (void)dealloc +{ + [self removeAllChildrenWithCleanup:YES]; + [userTetromino release]; + [difficultyLabel release]; + [scoreLabel release]; + [super dealloc]; +} +@end diff --git a/Classes/GameScene.h b/Classes/GameScene.h new file mode 100644 index 0000000..feaa5c2 --- /dev/null +++ b/Classes/GameScene.h @@ -0,0 +1,17 @@ +// +// GameScene.h +// Tetris +// +// Created by Joshua Aburto on 9/26/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import +#import "cocos2d.h" + + +@interface GameScene : Scene { + +} + +@end diff --git a/Classes/GameScene.m b/Classes/GameScene.m new file mode 100644 index 0000000..16186ff --- /dev/null +++ b/Classes/GameScene.m @@ -0,0 +1,37 @@ +// +// GameScene.m +// Tetris +// +// Created by Joshua Aburto on 9/26/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import "GameScene.h" +#import "GameLogicLayer.h" + + +@implementation GameScene + + +-(id) init +{ + if ((self = [super init])) { + Sprite *bg = [Sprite spriteWithFile:@"background.jpg"]; + [bg setPosition:ccp(160,240)]; + [self addChild:bg z:0]; + + Layer *layer = [GameLogicLayer node]; + [self addChild:layer z:1]; + + } + + return self; +} + +-(void) dealloc +{ + [self removeAllChildrenWithCleanup:YES]; + [[TextureMgr sharedTextureMgr] removeUnusedTextures]; + [super dealloc]; +} +@end diff --git a/Classes/TetrisAppDelegate.h b/Classes/TetrisAppDelegate.h new file mode 100755 index 0000000..b1aa913 --- /dev/null +++ b/Classes/TetrisAppDelegate.h @@ -0,0 +1,17 @@ +// +// TetrisAppDelegate.h +// Tetris +// +// Created by Joshua Aburto on 9/26/09. +// Copyright __MyCompanyName__ 2009. All rights reserved. +// + +#import + +@interface TetrisAppDelegate : NSObject { + UIWindow *window; +} + +@property (nonatomic, retain) UIWindow *window; + +@end diff --git a/Classes/TetrisAppDelegate.m b/Classes/TetrisAppDelegate.m new file mode 100755 index 0000000..b50d86e --- /dev/null +++ b/Classes/TetrisAppDelegate.m @@ -0,0 +1,76 @@ +// +// TetrisAppDelegate.m +// Tetris +// +// Created by Joshua Aburto on 9/26/09. +// Copyright __MyCompanyName__ 2009. All rights reserved. +// + +#import "TetrisAppDelegate.h" +#import "cocos2d.h" +#import "GameScene.h" + +@implementation TetrisAppDelegate + +@synthesize window; + +- (void) applicationDidFinishLaunching:(UIApplication*)application +{ + // Init the window + window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + + // cocos2d will inherit these values + [window setUserInteractionEnabled:YES]; + [window setMultipleTouchEnabled:YES]; + + // must be called before any othe call to the director + // WARNING: FastDirector doesn't interact well with UIKit controls + // [Director useFastDirector]; + + // before creating any layer, set the landscape mode + // [[Director sharedDirector] setDeviceOrientation:CCDeviceOrientationLandscapeLeft]; + // [[Director sharedDirector] setAnimationInterval:1.0/60]; + [[Director sharedDirector] setDisplayFPS:YES]; + + // Default texture format for PNG/BMP/TIFF/JPEG/GIF images + // It can be RGBA8888, RGBA4444, RGB5_A1, RGB565 + // You can change anytime. + [Texture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_RGBA4444]; + + // create an openGL view inside a window + [[Director sharedDirector] attachInView:window]; + [window makeKeyAndVisible]; + + // Seed random number generator. + srandom(time(NULL)); + [[Director sharedDirector] runWithScene: [GameScene node]]; +} + + +- (void)applicationWillResignActive:(UIApplication *)application { + [[Director sharedDirector] pause]; +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + [[Director sharedDirector] resume]; +} + +- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { + [[TextureMgr sharedTextureMgr] removeAllTextures]; +} + +- (void)applicationWillTerminate:(UIApplication *)application { + [[Director sharedDirector] end]; +} + +- (void)applicationSignificantTimeChange:(UIApplication *)application { + [[Director sharedDirector] setNextDeltaTimeZero:YES]; +} + +- (void)dealloc { + [[Director sharedDirector] release]; + [window release]; + [super dealloc]; +} + +@end diff --git a/Classes/Tetromino.h b/Classes/Tetromino.h new file mode 100644 index 0000000..b70be2b --- /dev/null +++ b/Classes/Tetromino.h @@ -0,0 +1,40 @@ +// +// Tetromino.h +// Tetris +// +// Created by Joshua Aburto on 9/28/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import "cocos2d.h" +#import "Block.h" + + +@interface Tetromino : CocosNode { + + enum tetrominoTypes { + kLetterI, + kLetterO, + kLetterS, + kLetterZ, + kLetterL, + kLetterJ, + kLetterT + } tetrominoType; + NSMutableArray *blockArray; + BOOL stuck; + + CGPoint leftMostPosition; + CGPoint rightMostPosition; +} + + +@property (retain, nonatomic) NSMutableArray *blockArray; +@property enum tetrominoTypes tetrominoType; +@property (assign) BOOL stuck; + +@property (readonly) CGPoint leftMostPosition; +@property (readonly) CGPoint rightMostPosition; + + +@end diff --git a/Classes/Tetromino.m b/Classes/Tetromino.m new file mode 100644 index 0000000..dbdf834 --- /dev/null +++ b/Classes/Tetromino.m @@ -0,0 +1,169 @@ +// +// Tetromino.m +// Tetris +// +// Created by Joshua Aburto on 9/28/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import "Tetromino.h" + +@interface Tetromino (private) +- (void)initializeTetromino; +- (void)setShape; +@end + +@implementation Tetromino +@synthesize blockArray; +@synthesize tetrominoType; + +- (id)init +{ + if ((self = [super init])) { + tetrominoType = (arc4random() % 7); + + blockArray = [[NSMutableArray alloc] initWithCapacity:4]; + + for (int i = 0; i < 4; i++) { + Block *tempBlock = [Block newBlock:tetrominoType]; + [blockArray addObject:tempBlock]; + [self addChild:tempBlock z:2]; + [tempBlock release]; + } + [self initializeTetromino]; + + } + return self; +} + +- (void)initializeTetromino +{ + self.stuck = NO; + [self setShape]; + + +} + +- (void)setShape +{ + NSUInteger index = 0; + for (Block *currentBlock in blockArray){ + + switch (tetrominoType) { + case kLetterI: + currentBlock.boardX = index+3; + currentBlock.boardY = 0; + break; + case kLetterO: + + if (index == 0 || index == 1) { + currentBlock.boardX = index+5; + currentBlock.boardY = 0; + } else { + currentBlock.boardX = index+3; + currentBlock.boardY = 1; + } + + break; + case kLetterS: + if (index == 0 || index == 1) { + currentBlock.boardX = index+6; + currentBlock.boardY = 0; + } else { + currentBlock.boardX = index+3; + currentBlock.boardY = 1; + } + break; + case kLetterZ: + if (index == 0 || index == 1) { + currentBlock.boardX = index+5; + currentBlock.boardY = 0; + } else { + currentBlock.boardX = index+4; + currentBlock.boardY = 1; + } + break; + case kLetterL: + if (index == 3) { + currentBlock.boardX = index+4; + currentBlock.boardY = 1; + } else { + currentBlock.boardX = index+5; + currentBlock.boardY = 0; + } + break; + case kLetterJ: + + if (index == 3) { + currentBlock.boardX = index+3; + currentBlock.boardY = 1; + } else { + currentBlock.boardX = index+5; + currentBlock.boardY = 0; + } + break; + case kLetterT: + if (index == 3) { + currentBlock.boardX = index+3; + currentBlock.boardY = 1; + } else { + currentBlock.boardX = index+5; + currentBlock.boardY = 0; + } + break; + default: + break; + } + currentBlock.position = COMPUTE_X_Y(currentBlock.boardX, currentBlock.boardY); + index++; + } + +} + +- (BOOL)stuck +{ + for (Block *block in blockArray) { + stuck = block.stuck; + } + return stuck; +} + +- (void)setStuck:(BOOL)stuckValue +{ + stuck = stuckValue; + for (Block *block in blockArray) { + block.stuck = stuckValue; + } +} + +- (void)dealloc +{ + [blockArray release]; + [super dealloc]; +} + + +- (CGPoint)leftMostPosition +{ + CGPoint myLeftPosition; + for (Block *currentBlock in blockArray) { + if ( myLeftPosition.x > currentBlock.boardX) { + myLeftPosition = ccp(currentBlock.boardX, currentBlock.boardY); + } + } + return myLeftPosition; + +} + +- (CGPoint)rightMostPosition +{ + CGPoint myRightPosition; + for (Block *currentBlock in blockArray) { + if (myRightPosition.x < currentBlock.boardX) { + myRightPosition = ccp(currentBlock.boardX, currentBlock.boardY); + } + } + return myRightPosition; +} + +@end diff --git a/CocosDenshion/CDAudioManager.h b/CocosDenshion/CDAudioManager.h new file mode 100644 index 0000000..22b088e --- /dev/null +++ b/CocosDenshion/CDAudioManager.h @@ -0,0 +1,125 @@ +/* CocosDenshion Audio Manager + * + * Copyright (C) 2009 Steve Oldmeadow + * + * For independent entities this program is free software; you can redistribute + * it and/or modify it under the terms of the 'cocos2d for iPhone' license with + * the additional proviso that 'cocos2D for iPhone' must be credited in a manner + * that can be be observed by end users, for example, in the credits or during + * start up. Failure to include such notice is deemed to be acceptance of a + * non independent license (see below). + * + * For the purpose of this software non independent entities are defined as + * those where the annual revenue of the entity employing, partnering, or + * affiliated in any way with the Licensee is greater than $250,000 USD annually. + * + * Non independent entities may license this software or a derivation of it + * by a donation of $500 USD per application to the cocos2d for iPhone project. + * + * 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. + */ + +#import "CocosDenshion.h" +#import + +/** Different modes of the engine */ +typedef enum { + kAudioManagerFxOnly, //!Other apps will be able to play audio + kAudioManagerFxPlusMusic, //!Only this app will play audio + kAudioManagerFxPlusMusicIfNoOtherAudio //!If another app is playing audio at start up then allow it to continue and don't play music +} 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; + + +@interface CDAsynchInitialiser : NSOperation {} +@end + +/** CDAudioManager is a wrapper around AVAudioPlayer. + 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 + @since v0.8 + */ +@interface CDAudioManager : NSObject { + CDSoundEngine *soundEngine; + AVAudioPlayer *backgroundMusic; + NSString *lastBackgroundMusicFilePath; + UInt32 _audioSessionCategory; + BOOL _audioWasPlayingAtStartup; + tAudioManagerMode _mode; + SEL backgroundMusicCompletionSelector; + id backgroundMusicCompletionListener; + BOOL willPlayBackgroundMusic; + BOOL _mute; + BOOL _muteStoppedMusic; + + //For handling resign/become active + BOOL _isObservingAppEvents; + BOOL _systemPausedMusic; + tAudioManagerResignBehavior _resignBehavior; + NSTimeInterval _bookmark; + + +} + +@property (readonly) CDSoundEngine *soundEngine; +@property (readonly) AVAudioPlayer *backgroundMusic; +@property (readonly) BOOL willPlayBackgroundMusic; +@property (readwrite) BOOL mute; + +/** Returns the shared singleton */ ++ (CDAudioManager *) sharedManager; ++ (tAudioManagerState) sharedManagerState; +/** Configures the shared singletong with a mode, a channel definition and a total number of channels */ ++ (void) configure: (tAudioManagerMode) mode channelGroupDefinitions:(int[]) channelGroupDefinitions channelGroupTotal:(int) channelGroupTotal; +/** Initializes the engine asynchronously with a mode, channel definition and a total number of channels */ ++ (void) initAsynchronously: (tAudioManagerMode) mode channelGroupDefinitions:(int[]) channelGroupDefinitions channelGroupTotal:(int) channelGroupTotal; +/** Initializes the engine synchronously with a mode, channel definition and a total number of channels */ +- (id) init: (tAudioManagerMode) mode channelGroupDefinitions:(int[]) channelGroupDefinitions channelGroupTotal:(int) channelGroupTotal; +/** Plays music in background. The music can be looped or not + It is recommended to use .mp3 files as background 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; +/** Stops the background music. The music can also be released from the cache */ +-(void) stopBackgroundMusic:(BOOL) release; +/** 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; +-(void) audioSessionInterrupted; +-(void) audioSessionResumed; +-(void) setResignBehavior:(tAudioManagerResignBehavior) resignBehavior autoHandle:(BOOL) autoHandle; + +@end diff --git a/CocosDenshion/CDAudioManager.m b/CocosDenshion/CDAudioManager.m new file mode 100644 index 0000000..87c6574 --- /dev/null +++ b/CocosDenshion/CDAudioManager.m @@ -0,0 +1,492 @@ +/* CocosDenshion Audio Manager + * + * Copyright (C) 2009 Steve Oldmeadow + * + * For independent entities this program is free software; you can redistribute + * it and/or modify it under the terms of the 'cocos2d for iPhone' license with + * the additional proviso that 'cocos2D for iPhone' must be credited in a manner + * that can be be observed by end users, for example, in the credits or during + * start up. Failure to include such notice is deemed to be acceptance of a + * non independent license (see below). + * + * For the purpose of this software non independent entities are defined as + * those where the annual revenue of the entity employing, partnering, or + * affiliated in any way with the Licensee is greater than $250,000 USD annually. + * + * Non independent entities may license this software or a derivation of it + * by a donation of $500 USD per application to the cocos2d for iPhone project. + * + * 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. + */ + +#import "CDAudioManager.h" +#import "ccMacros.h" + +//Audio session interruption callback - used if sound engine is +//handling audio session interruption automatically +extern void managerInterruptionCallback (void *inUserData, UInt32 interruptionState ) { + CDAudioManager *controller = (CDAudioManager *) inUserData; + if (interruptionState == kAudioSessionBeginInterruption) { + [controller audioSessionInterrupted]; + } else if (interruptionState == kAudioSessionEndInterruption) { + [controller audioSessionResumed]; + } +} + +#if TARGET_IPHONE_SIMULATOR +//Workaround for issue in simulator. +//See: audioPlayerDidFinishPlaying +float realBackgroundMusicVolume = -1.0f; +#endif + + +//NSOperation object used to asynchronously initialise +@implementation CDAsynchInitialiser + +-(void) main { + [super main]; + [CDAudioManager sharedManager]; +} + +@end + +@implementation CDAudioManager +@synthesize soundEngine, backgroundMusic, willPlayBackgroundMusic; +static CDAudioManager *sharedManager; +static tAudioManagerState _sharedManagerState = kAMStateUninitialised; +static tAudioManagerMode configuredMode; +static int *configuredChannelGroupDefinitions; +static int configuredChannelGroupTotal; +static BOOL configured = FALSE; + +// Init ++ (CDAudioManager *) sharedManager +{ + @synchronized(self) { + if (!sharedManager) { + if (!configured) { + //Set defaults here + configuredMode = kAudioManagerFxPlusMusicIfNoOtherAudio; + //Just create one channel group with all the sources + //configuredChannelGroupDefinitions = new int[1]; + configuredChannelGroupDefinitions = (int *)malloc( sizeof(configuredChannelGroupDefinitions[0]) * 1); + configuredChannelGroupDefinitions[0] = CD_MAX_SOURCES; + configuredChannelGroupTotal = 1; + } + [[CDAudioManager alloc] init:configuredMode channelGroupDefinitions:configuredChannelGroupDefinitions channelGroupTotal:configuredChannelGroupTotal]; + _sharedManagerState = kAMStateInitialised;//This is only really relevant when using asynchronous initialisation + } + return sharedManager; + } + return nil; +} + ++ (tAudioManagerState) sharedManagerState { + return _sharedManagerState; +} + +/** + * Call this to set up audio manager asynchronously. Initialisation is finished when sharedManagerState == kAMStateInitialised + */ ++ (void) initAsynchronously: (tAudioManagerMode) mode channelGroupDefinitions:(int[]) channelGroupDefinitions channelGroupTotal:(int) channelGroupTotal { + @synchronized(self) { + if (_sharedManagerState == kAMStateUninitialised) { + _sharedManagerState = kAMStateInitialising; + [CDAudioManager configure:mode channelGroupDefinitions:channelGroupDefinitions channelGroupTotal:channelGroupTotal]; + 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."); + sharedManager = [super alloc]; + return sharedManager; + } + return nil; +} + +/* + * Call this method before accessing the shared manager in order to configure the shared audio manager + */ ++ (void) configure: (tAudioManagerMode) mode channelGroupDefinitions:(int[]) channelGroupDefinitions channelGroupTotal:(int) channelGroupTotal { + configuredMode = mode; + //configuredChannelGroupDefinitions = new int[channelGroupTotal]; + //NB: memory leak here if configure is called more than once, it is not intended to be used that way though (SO). + configuredChannelGroupDefinitions = (int *)malloc( sizeof(configuredChannelGroupDefinitions[0]) * channelGroupTotal); + if(!configuredChannelGroupDefinitions) { + CCLOG(@"Denshion: configuredChannelGroupDefinitions memory allocation failed"); + } + + for (int i=0; i < channelGroupTotal; i++) { + configuredChannelGroupDefinitions[i] = channelGroupDefinitions[i]; + } + configuredChannelGroupTotal = channelGroupTotal; + configured = TRUE; +} + + +- (id) init: (tAudioManagerMode) mode channelGroupDefinitions:(int[]) channelGroupDefinitions channelGroupTotal:(int) channelGroupTotal { + if ((self = [super init])) { + //Initialise the audio session + AudioSessionInitialize(NULL, NULL,managerInterruptionCallback, self); + + _mode = mode; + backgroundMusicCompletionSelector = nil; + _isObservingAppEvents = FALSE; + _systemPausedMusic = FALSE; + _muteStoppedMusic = FALSE; + + switch (_mode) { + + case kAudioManagerFxOnly: + //Share audio with other app + CCLOG(@"Denshion: Audio will be shared"); + _audioSessionCategory = kAudioSessionCategory_AmbientSound; + willPlayBackgroundMusic = FALSE; + break; + + case kAudioManagerFxPlusMusic: + //Use audio exclusively - if other audio is playing it will be stopped + CCLOG(@"Denshion: Audio will be exclusive"); + _audioSessionCategory = kAudioSessionCategory_SoloAmbientSound; + willPlayBackgroundMusic = TRUE; + break; + + default: + //kAudioManagerFxPlusMusicIfNoOtherAudio + CCLOG(@"Denshion: Checking for other audio"); + UInt32 isPlaying; + UInt32 varSize = sizeof(isPlaying); + AudioSessionGetProperty (kAudioSessionProperty_OtherAudioIsPlaying, &varSize, &isPlaying); + if (isPlaying != 0) { + CCLOG(@"Denshion: Other audio is playing audio will be shared"); + _audioSessionCategory = kAudioSessionCategory_AmbientSound; + willPlayBackgroundMusic = FALSE; + _audioWasPlayingAtStartup = TRUE; + } else { + CCLOG(@"Denshion: Other audio is not playing audio will be exclusive"); + _audioSessionCategory = kAudioSessionCategory_SoloAmbientSound; + willPlayBackgroundMusic = TRUE; + _audioWasPlayingAtStartup = FALSE; + } + + break; + } + + //Set audio session category + if (willPlayBackgroundMusic) { + //Work around to ensure background music is not decoded in software + //on OS 3.0. Thanks to Bryan Acceleroto (SO 2009.07.02) + UInt32 fakeCategory = kAudioSessionCategory_MediaPlayback; + AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(fakeCategory), &fakeCategory); + AudioSessionSetActive(TRUE); + AudioSessionSetActive(FALSE); + } + AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(_audioSessionCategory), &_audioSessionCategory); + AudioSessionSetActive(TRUE); + soundEngine = [[CDSoundEngine alloc] init:channelGroupDefinitions channelGroupTotal:channelGroupTotal]; + } + return self; +} + +-(void) dealloc { + [self stopBackgroundMusic]; + [lastBackgroundMusicFilePath release]; + [soundEngine release]; + if (_isObservingAppEvents) { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + } + [super dealloc]; +} + +-(BOOL) isBackgroundMusicPlaying { + if (backgroundMusic != nil) { + return backgroundMusic.isPlaying; + } else { + return FALSE; + } +} + +-(BOOL) mute { + return _mute; +} + +/** + * Setting mute to true will stop all sounds currently playing and prevent further sounds from playing. + * If background music was playing when sound was muted it will be resumed when sound is unmuted. + */ +-(void) setMute:(BOOL) muteValue { + + [soundEngine setMute:muteValue]; + _mute = muteValue; + if (_mute) { + if ([self isBackgroundMusicPlaying]) { + [self stopBackgroundMusic:FALSE]; + _muteStoppedMusic = TRUE; + } else { + _muteStoppedMusic = FALSE; + } + } else { + if (_muteStoppedMusic) { + [self resumeBackgroundMusic]; + _muteStoppedMusic = FALSE; + } + } +} + +//Load background music ready for playing +-(void) preloadBackgroundMusic:(NSString*) filePath +{ + if (!willPlayBackgroundMusic) { + CCLOG(@"Denshion: preload background music aborted because audio is not exclusive"); + return; + } + + if (![filePath isEqualToString:lastBackgroundMusicFilePath]) { + CCLOG(@"Denshion: loading new or different background music file %@", filePath); + if(backgroundMusic != nil) + { + [self stopBackgroundMusic:TRUE]; + } + NSString *path = [FileUtils fullPathFromRelativePath:filePath]; + backgroundMusic = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL]; + + if (backgroundMusic != nil) { + [backgroundMusic prepareToPlay]; + backgroundMusic.delegate = self; + } + lastBackgroundMusicFilePath = [filePath copy]; + } +} + +-(void) playBackgroundMusic:(NSString*) filePath loop:(BOOL) loop +{ + if (!willPlayBackgroundMusic || _mute) { + CCLOG(@"Denshion: play bgm aborted because audio is not exclusive or sound is muted"); + return; + } + + if (![filePath isEqualToString:lastBackgroundMusicFilePath]) { + [self preloadBackgroundMusic:filePath]; + backgroundMusic.numberOfLoops = (loop ? -1:0); + [backgroundMusic play]; + } else { + CCLOG(@"Denshion: request to play current background music file"); + [self pauseBackgroundMusic]; + [self rewindBackgroundMusic]; +#if TARGET_IPHONE_SIMULATOR + //Workaround for issue in simulator. + //See: audioPlayerDidFinishPlaying + //Need to restore volume and loop count to correct values + if (realBackgroundMusicVolume >= 0.0f) { + backgroundMusic.volume = realBackgroundMusicVolume; + } +#endif + backgroundMusic.numberOfLoops = (loop ? -1:0);//Reset loop count because track may have been preloaded + [backgroundMusic play]; + } +} + +//Kept for backwards compatibility with 1.5 interface +-(void) stopBackgroundMusic +{ + [self stopBackgroundMusic:TRUE]; +} + +//@param release - if TRUE AVAudioPlayer instance will be released +-(void) stopBackgroundMusic:(BOOL) release +{ + if (backgroundMusic != nil) { + [backgroundMusic stop]; + if (release) { + [backgroundMusic autorelease]; + backgroundMusic = nil; + lastBackgroundMusicFilePath = nil; + } + } +} + +-(void) pauseBackgroundMusic +{ + if (backgroundMusic != nil) { + [backgroundMusic pause]; + } +} + +-(void) resumeBackgroundMusic +{ + if (backgroundMusic != nil) { + [backgroundMusic play]; + } +} + +-(void) rewindBackgroundMusic +{ + if (backgroundMusic != nil) { + backgroundMusic.currentTime = 0;; + } +} + +-(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player { + CCLOG(@"Denshion: audio player interrupted"); +} + +-(void)audioPlayerEndInterruption:(AVAudioPlayer *)player { + CCLOG(@"Denshion: audio player resumed"); + [player play]; +} + +-(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. + * + * 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; +} + +//Called when application resigns active only if setResignBehavior has been called +- (void) applicationWillResignActive:(NSNotification *) notification +{ + + switch (_resignBehavior) { + + case kAMRBStopPlay: + if (backgroundMusic.isPlaying) { + _systemPausedMusic = TRUE; + [self stopBackgroundMusic:FALSE]; + } else { + //Music is either paused or stopped, if it is paused it will be restarted + //by OS so we will stop it. + _systemPausedMusic = FALSE; + [self stopBackgroundMusic:FALSE]; + } + break; + + case kAMRBStop: + //Stop music regardless of whether it is playing or not because if it was paused + //then the OS would resume it + [self stopBackgroundMusic:FALSE]; + + default: + break; + + } + + CCLOG(@"Denshion: audio manager handling resign active"); +} + +//Called when application becomes active only if setResignBehavior has been called +- (void) applicationDidBecomeActive:(NSNotification *) notification +{ + + switch (_resignBehavior) { + + case kAMRBStopPlay: + if (_systemPausedMusic) { + //Music had been stopped but stop maintains current time + //so playing again will continue from where music was before resign active + [self resumeBackgroundMusic]; + _systemPausedMusic = FALSE; + } + break; + + default: + break; + + } + CCLOG(@"Denshion: audio manager handling become active"); + +} + +//Called when application terminates only if setResignBehavior has been called +- (void) applicationWillTerminate:(NSNotification *) notification +{ + CCLOG(@"Denshion: audio manager handling terminate"); + [self stopBackgroundMusic]; +} + + +- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag { + CCLOG(@"Denshion: audio player finished"); + if (backgroundMusicCompletionSelector != nil) { + [backgroundMusicCompletionListener performSelector:backgroundMusicCompletionSelector]; + } +#if TARGET_IPHONE_SIMULATOR + CCLOG(@"Denshion: workaround for OpenAL clobbered audio issue"); + //This is a workaround for an issue in the 2.2 and 2.2.1 simulator. 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. + player.numberOfLoops = -1; + realBackgroundMusicVolume = player.volume; + player.volume = 0; + [player play]; +#endif +} + + +//Code to handle audio session interruption. Thanks to Andy Fitter and Ben Britten. +-(void)audioSessionInterrupted +{ + CCLOG(@"Denshion: Audio session interrupted"); + ALenum error = AL_NO_ERROR; + // Deactivate the current audio session + AudioSessionSetActive(NO); + // set the current context to NULL will 'shutdown' openAL + alcMakeContextCurrent(NULL); + if((error = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: Error making context current %x\n", error); + } + // now suspend your context to 'pause' your sound world + alcSuspendContext([soundEngine openALContext]); + if((error = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: Error suspending context %x\n", error); + } +} + +//Code to handle audio session resumption. Thanks to Andy Fitter and Ben Britten. +-(void)audioSessionResumed +{ + ALenum error = AL_NO_ERROR; + CCLOG(@"Denshion: Audio session resumed"); + // Reset audio session + OSStatus result = AudioSessionSetProperty ( kAudioSessionProperty_AudioCategory, sizeof(_audioSessionCategory), &_audioSessionCategory ); + + // Reactivate the current audio session + result = AudioSessionSetActive(YES); + + // Restore open al context + alcMakeContextCurrent([soundEngine openALContext]); + if((error = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: Error making context current%x\n", error); + } + // 'unpause' my context + alcProcessContext([soundEngine openALContext]); + if((error = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: Error processing context%x\n", error); + } +} + +@end diff --git a/CocosDenshion/CDOpenALSupport.h b/CocosDenshion/CDOpenALSupport.h new file mode 100644 index 0000000..42f092a --- /dev/null +++ b/CocosDenshion/CDOpenALSupport.h @@ -0,0 +1,253 @@ +/* + + 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. + + */ + +/* + 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. + */ + +#import +#import +#import +#import +#import "ccMacros.h" + + +//Taken from oalTouch MyOpenALSupport 1.1 +void* loadWaveAudioData(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) { CCLOG(@"MyGetOpenALAudioData: AudioFileOpenURL FAILED, Error = %ld\n", err); goto Exit; } + + // Get the audio data format + err = AudioFileGetProperty(afid, kAudioFilePropertyDataFormat, &thePropertySize, &theFileFormat); + if(err) { CCLOG(@"MyGetOpenALAudioData: AudioFileGetProperty(kAudioFileProperty_DataFormat) FAILED, Error = %ld\n", err); goto Exit; } + + if (theFileFormat.mChannelsPerFrame > 2) { + CCLOG(@"MyGetOpenALAudioData - Unsupported Format, channel count is greater than stereo\n"); goto Exit; + } + + if ((theFileFormat.mFormatID != kAudioFormatLinearPCM) || (!TestAudioFormatNativeEndian(theFileFormat))) { + CCLOG(@"MyGetOpenALAudioData - Unsupported Format, must be little-endian PCM\n"); goto Exit; + } + + if ((theFileFormat.mBitsPerChannel != 8) && (theFileFormat.mBitsPerChannel != 16)) { + CCLOG(@"MyGetOpenALAudioData - Unsupported Format, must be 8 or 16 bit PCM\n"); goto Exit; + } + + + thePropertySize = sizeof(fileDataSize); + err = AudioFileGetProperty(afid, kAudioFilePropertyAudioDataByteCount, &thePropertySize, &fileDataSize); + if(err) { CCLOG(@"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 + CCLOG(@"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* loadCafAudioData(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) + { + CCLOG(@"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) + { + CCLOG(@"MyGetOpenALAudioData: ExtAudioFileGetProperty(kExtAudioFileProperty_FileDataFormat) FAILED, Error = %ld\n", status); + abort = YES; + } + if (abort) + goto Exit; + + if (theFileFormat.mChannelsPerFrame > 2) + { + CCLOG(@"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) + { + CCLOG(@"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) + { + CCLOG(@"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 + CCLOG(@"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* MyGetOpenALAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate) { + + CFStringRef extension = CFURLCopyPathExtension(inFileURL); + CFComparisonResult isWavFile = CFStringCompare (extension,(CFStringRef)@"wav", kCFCompareCaseInsensitive); + if (extension != NULL) { + CFRelease(extension); + } + + if (isWavFile == kCFCompareEqualTo) { + return loadWaveAudioData(inFileURL, outDataSize, outDataFormat, outSampleRate); + } else { + return loadCafAudioData(inFileURL, outDataSize, outDataFormat, outSampleRate); + } +} + diff --git a/CocosDenshion/CocosDenshion.h b/CocosDenshion/CocosDenshion.h new file mode 100644 index 0000000..f697c73 --- /dev/null +++ b/CocosDenshion/CocosDenshion.h @@ -0,0 +1,242 @@ +/* CocosDenshion Sound Engine + * + * Copyright (C) 2009 Steve Oldmeadow + * + * For independent entities this program is free software; you can redistribute + * it and/or modify it under the terms of the 'cocos2d for iPhone' license with + * the additional proviso that 'cocos2D for iPhone' must be credited in a manner + * that can be be observed by end users, for example, in the credits or during + * start up. Failure to include such notice is deemed to be acceptance of a + * non independent license (see below). + * + * For the purpose of this software non independent entities are defined as + * those where the annual revenue of the entity employing, partnering, or + * affiliated in any way with the Licensee is greater than $250,000 USD annually. + * + * Non independent entities may license this software or a derivation of it + * by a donation of $500 USD per application to the cocos2d for iPhone project. + * + * 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. + */ + +/** +@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 + +*/ + +/* Changelog +2.0 (2009.07.09) * Change all file names to file paths to support loading sounds from locations other than root. + (Thanks to Jason Cecil) + * Take out C++ dependencies to make it easy to include CocosDenshion in static libraries. +1.6 (2009.07.02) * Added looping property to CDSourceWrapper so that looping flag can be toggled during playback + (Thanks to Pablo Ruiz) + * Added fix to ensure mp3 files are not decoded in software on 3.0 (Thanks to Bryan Accleroto) + * Added mute to CDAudioManager + * Added handlers for resign active and become active to CDAudioManager (Thanks to Dominique Bongard) + * Added stopBackgroundMusic method with flag to indicate whether resources should be released + (Thanks to Dominique Bongard) + * Added functionality to mute channel groups +1.5 (2009.06.13) * Added preLoadBackgroundMusic method to CDAudioManager to allow background music to be preloaded + * Fixed bug with sound engine locking up when trying to load non existent file asynchronously + (Thanks to Edison's Labs for reporting) +1.4 (2009.06.10) * Implemented asynchronous initialisation of audio manager + * Implemented asynchronous loading of sound buffers + * Fixed problem with mute button being ignored if game played background music (Thanks to Sebastien Flory for reporting) +1.3 (2009.06.02) * Added non interruptible option for channel group + * Added loop parameter for playing background music + * Added isPlaying property to CDSourceWrapper + * Modified CDSourceWrapper to return last set values of pitch, pan and gain + * Added option of specifying callback selector for background music completion to CDAudioManager + * Workaround for issue in 2.2 & 2.2.1 simulator whereby OpenAL playback would be killed after + an AVAudioPlayer (backgroundMusic) stops. +1.2 (2009.05.27) * Changes for integration with cocos2d svn repository. + * Renamed myOpenALSupport.h to CDOpenALSupport.h to distinguish it from the + version included with the Aeterius sound engine. + * Added unloadBuffer method. + * Updated myOpenALSupport.h to latest version with support for IMA4 compressed files +1.1 (2009.05.26) * Added code for handling audio session interruption. Thanks to Andy Fitter and + Ben Britten for the code. +1.0 (2009.05.01) * Initial release +*/ + +#import +#import +#import +#import +#import "FileUtils.h" + +//You may want to edit these. Devices won't support any more than 32 sources though. +#define CD_MAX_BUFFERS 32 //Total number of sounds that can be loaded +#define CD_MAX_SOURCES 32 //Total number of playback channels that can be created (32 is the limit) + +#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_CHANNEL_GROUP_NON_INTERRUPTIBLE 0xFEDEEFF //User internally to indicate channel group is not interruptible +#define CD_MUTE 0xFEEDBAB //Return value indicating sound engine is muted or non functioning + +enum bufferState { + CD_BS_EMPTY = 0, + CD_BS_LOADED = 1, + CD_BS_FAILED = 2 +}; + +typedef struct _channelGroup { + int startIndex; + int endIndex; + int currentIndex; + bool mute; +} channelGroup; + + +//////////////////////////////////////////////////////////////////////////// + +/** 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 + */ +@interface CDSoundEngine : NSObject { + + ALuint *_sources; + ALuint *_buffers; + int *_bufferStates; + ALuint *_sourceBufferAttachments; + channelGroup *_channelGroups; + ALCcontext *context; + int _channelGroupTotal; + int _channelTotal; + UInt32 _audioSessionCategory; + BOOL _handleAudioSession; + BOOL _mute; + + ALenum lastErrorCode; + BOOL functioning; + float asynchLoadProgress; + +} + +@property (readwrite, nonatomic) ALfloat masterGain; +@property (readwrite, nonatomic) BOOL mute; +@property (readonly) ALenum lastErrorCode;//Last OpenAL error code that was generated +@property (readonly) BOOL functioning;//Is the sound engine functioning +@property (readwrite) float asynchLoadProgress; + +/** Initializes the engine with a group definition and a total number of groups */ +- (id)init:(int[]) channelGroupDefinitions channelGroupTotal:(int) channelGroupTotal; +/** Initializes the engine with a group definition, a total number of groups and an audio session category */ +- (id)init:(int[]) channelGroupDefinitions channelGroupTotal:(int) channelGroupTotal audioSessionCategory:(UInt32) audioSessionCategory; + +/** Plays a sound in a channel group with a pitch, pan and gain. The sound could played looped or not */ +- (ALuint) playSound:(int) soundId channelGroupId:(int)channelGroupId pitch:(float) pitch pan:(float) pan gain:(float) gain loop:(BOOL) loop; + +/** Stops playing a sound */ +- (void) stopSound:(ALuint) sourceId; +/** Stops playing a channel group */ +- (void) stopChannelGroup:(int) channelGroupId; +- (void) setChannelGroupNonInterruptible:(int) channelGroupId isNonInterruptible:(BOOL) isNonInterruptible; +- (void) setChannelGroupMute:(int) channelGroupId mute:(BOOL) mute; +- (BOOL) channelGroupMute:(int) channelGroupId; +- (BOOL) loadBuffer:(int) soundId filePath:(NSString*) filePath; +- (void) loadBuffersAsynchronously:(NSArray *) loadRequests; +- (BOOL) unloadBuffer:(int) soundId; +- (ALCcontext *) openALContext; +- (void) audioSessionInterrupted; +- (void) audioSessionResumed; + +- (BOOL) _initOpenAL; +- (ALuint) _startSound:(int) soundId channelId:(int) channelId pitchVal:(float) pitchVal panVal:(float) panVal gainVal:(float) gainVal looping:(BOOL) looping checkState:(BOOL) checkState +; + +@end + +//////////////////////////////////////////////////////////////////////////// +@interface CDSourceWrapper : NSObject { + ALuint sourceId; + float lastPitch; + float lastPan; + float lastGain; + BOOL lastLooping; +} +@property (readwrite, nonatomic) ALuint sourceId; +@property (readwrite, nonatomic) float pitch; +@property (readwrite, nonatomic) float gain; +@property (readwrite, nonatomic) float pan; +@property (readwrite, nonatomic) BOOL looping; +@property (readonly) BOOL isPlaying; + +@end + +//////////////////////////////////////////////////////////////////////////// +@interface CDAsynchBufferLoader : NSOperation { + NSArray *_loadRequests; + CDSoundEngine *_soundEngine; +} + +-(id) init:(NSArray *)loadRequests soundEngine:(CDSoundEngine *) theSoundEngine; + +@end + +//////////////////////////////////////////////////////////////////////////// + +@interface CDBufferLoadRequest: NSObject +{ + NSString *filePath; + int soundId; + //id loader; +} + +@property (readonly) NSString *filePath; +@property (readonly) int soundId; + +- (id)init:(int) theSoundId filePath:(NSString *) theFilePath; + +@end diff --git a/CocosDenshion/CocosDenshion.m b/CocosDenshion/CocosDenshion.m new file mode 100644 index 0000000..7a1cc42 --- /dev/null +++ b/CocosDenshion/CocosDenshion.m @@ -0,0 +1,755 @@ +/* CocosDenshion Sound Engine + * + * Copyright (C) 2009 Steve Oldmeadow + * + * For independent entities this program is free software; you can redistribute + * it and/or modify it under the terms of the 'cocos2d for iPhone' license with + * the additional proviso that 'cocos2D for iPhone' must be credited in a manner + * that can be be observed by end users, for example, in the credits or during + * start up. Failure to include such notice is deemed to be acceptance of a + * non independent license (see below). + * + * For the purpose of this software non independent entities are defined as + * those where the annual revenue of the entity employing, partnering, or + * affiliated in any way with the Licensee is greater than $250,000 USD annually. + * + * Non independent entities may license this software or a derivation of it + * by a donation of $500 USD per application to the cocos2d for iPhone project. + * + * 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. + */ + +#import "CocosDenshion.h" +#import "CDOpenALSupport.h" +#import "ccMacros.h" + +//Audio session interruption callback - used if sound engine is +//handling audio session interruption automatically +extern void interruptionListenerCallback (void *inUserData, UInt32 interruptionState ) { + CDSoundEngine *controller = (CDSoundEngine *) inUserData; + if (interruptionState == kAudioSessionBeginInterruption) { + [controller audioSessionInterrupted]; + } else if (interruptionState == kAudioSessionEndInterruption) { + [controller audioSessionResumed]; + } +} + +@implementation CDSoundEngine + +@synthesize lastErrorCode, functioning, asynchLoadProgress; + +/** + * Internal method called during init + */ +- (BOOL) _initOpenAL +{ + //ALenum error; + context = NULL; + ALCdevice *newDevice = NULL; + + //_buffers = new ALuint[CD_MAX_BUFFERS]; + _buffers = (ALuint *)malloc( sizeof(_buffers[0]) * CD_MAX_BUFFERS ); + if(!_buffers) { + CCLOG(@"Denshion: buffer memory allocation failed"); + return FALSE; + } + + //_sources = new ALuint[CD_MAX_SOURCES]; + _sources = (ALuint *)malloc( sizeof(_sources[0]) * CD_MAX_SOURCES ); + if(!_sources) { + CCLOG(@"Denshion: source memory allocation failed"); + return FALSE; + } + + // 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); + + //Don't want a distance model + alDistanceModel(AL_NONE); + + // Create some OpenAL Buffer Objects + alGenBuffers(CD_MAX_BUFFERS, _buffers); + if((lastErrorCode = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: Error Generating Buffers: %x", lastErrorCode); + return FALSE;//No buffers + } + + // Create some OpenAL Source Objects + alGenSources(CD_MAX_SOURCES, _sources); + if((lastErrorCode = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: Error generating sources! %x\n", lastErrorCode); + return FALSE;//No sources + } + + } + } else { + return FALSE;//No device + } + alGetError();//Clear error + return TRUE; +} + +- (void) dealloc { + ALCcontext *currentContext = NULL; + ALCdevice *device = NULL; + + + CCLOG(@"Denshion: Deallocing sound engine."); + free(_bufferStates); + free(_channelGroups); + free(_sourceBufferAttachments); + + // Delete the Sources + alDeleteSources(CD_MAX_SOURCES, _sources); + if((lastErrorCode = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: Error deleting sources! %x\n", lastErrorCode); + } + // Delete the Buffers + alDeleteBuffers(CD_MAX_BUFFERS, _buffers); + if((lastErrorCode = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: Error deleting buffers! %x\n", lastErrorCode); + } + + //Get active context + currentContext = alcGetCurrentContext(); + //Get device for active context + device = alcGetContextsDevice(currentContext); + //Release context + alcDestroyContext(currentContext); + //Close device + alcCloseDevice(device); + + free(_buffers); + free(_sources); + [super dealloc]; +} + +/** + * Call this initialiser if you want the sound engine to automatically handle audio session interruption. + * If you are using the sound engine in conjunction with another audio api such as AVAudioPlayer or + * AudioQueue then you probably do not want the sound engine to handle audio session interruption + * for you. + * + * The audioSessionCategory should be one of the audio session category enumeration values such as + * kAudioSessionCategory_AmbientSound. Your choice is dependent on how you want your audio to interact + * with other audio on the device. + * + * Please note that audio session interruption is different to application interruption. Known triggers are + * alarm notification from clock, incoming phone call that is rejected and video playback ending. + */ +- (id)init:(int[]) channelGroupDefinitions channelGroupTotal:(int) channelGroupTotal audioSessionCategory:(UInt32) audioSessionCategory +{ + if ((self = [super init])) { + + _mute = FALSE; + asynchLoadProgress = 0.0f; + _audioSessionCategory = audioSessionCategory; + _handleAudioSession = (_audioSessionCategory != CD_IGNORE_AUDIO_SESSION); + if (_handleAudioSession) { + CCLOG(@"Denshion: Sound engine will handle audio session interruption"); + //Set up audio session + OSStatus result = AudioSessionInitialize(NULL, NULL,interruptionListenerCallback, self); + result = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(_audioSessionCategory), &_audioSessionCategory); + } + + //Set up channel groups + //_channelGroups = new channelGroup[channelGroupTotal]; + _channelGroups = (channelGroup *)malloc( sizeof(_channelGroups[0]) * channelGroupTotal); + if(!_channelGroups) { + CCLOG(@"Denshion: channel groups memory allocation failed"); + } + + _channelGroupTotal = channelGroupTotal; + int channelCount = 0; + for (int i=0; i < channelGroupTotal; i++) { + + _channelGroups[i].startIndex = channelCount; + _channelGroups[i].endIndex = _channelGroups[i].startIndex + channelGroupDefinitions[i] - 1; + _channelGroups[i].currentIndex = _channelGroups[i].startIndex; + _channelGroups[i].mute = false; + channelCount += channelGroupDefinitions[i]; + CCLOG(@"Denshion: channel def %i %i %i %i",i,_channelGroups[i].startIndex, _channelGroups[i].endIndex, _channelGroups[i].currentIndex); + } + + NSAssert(channelCount <= CD_MAX_SOURCES,@"requested total channels exceeds CD_MAX_SOURCES"); + _channelTotal = channelCount; + + //Set up buffer states + //_bufferStates = new int[CD_MAX_BUFFERS]; + _bufferStates = (int *)malloc( sizeof(_bufferStates[0]) * CD_MAX_BUFFERS); + if(!_bufferStates) { + CCLOG(@"Denshion: buffer states memory allocation failed"); + } + + for (int i=0; i < CD_MAX_BUFFERS; i++) { + _bufferStates[i] = CD_BS_EMPTY; + } + + //_sourceBufferAttachments = new ALuint[CD_MAX_SOURCES]; + _sourceBufferAttachments = (ALuint *)malloc( sizeof(_sourceBufferAttachments[0]) * CD_MAX_SOURCES); + if(!_sourceBufferAttachments) { + CCLOG(@"Denshion: source buffer attachments memory allocation failed"); + } + + // Initialize our OpenAL environment + if ([self _initOpenAL]) { + functioning = TRUE; + } else { + //Something went wrong with OpenAL + functioning = FALSE; + } + + + } + + return self; +} + +/** + * If you call this initialiser the sound engine won't handle audio session interruption and resumption. + */ +- (id)init:(int[]) channelGroupDefinitions channelGroupTotal:(int) channelGroupTotal { + return [self init:channelGroupDefinitions channelGroupTotal:channelGroupTotal audioSessionCategory:CD_IGNORE_AUDIO_SESSION]; +} + +/** + * Delete the buffer identified by soundId + * @return true if buffer deleted successfully, otherwise false + */ +- (BOOL) unloadBuffer:(int) soundId +{ + //Before a buffer can be deleted any sources that are attached to it must be stopped + for (int i=0; i < _channelTotal; 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] == _sourceBufferAttachments[i]) { + + CCLOG(@"Denshion: Found attached source %i %i %i",i,_buffers[soundId],_sourceBufferAttachments[i]); + + //Stop source and detach + alSourceStop(_sources[i]); + if((lastErrorCode = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: error stopping source: %x\n", lastErrorCode); + } + + alSourcei(_sources[i], AL_BUFFER, 0);//Attach to "NULL" buffer to detach + if((lastErrorCode = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: error detaching buffer: %x\n", lastErrorCode); + } else { + //Record that source is now attached to nothing + _sourceBufferAttachments[i] = 0; + } + } + } + + alDeleteBuffers(1, &_buffers[soundId]); + if((lastErrorCode = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: error deleting buffer: %x\n", lastErrorCode); + _bufferStates[soundId] = CD_BS_FAILED; + return FALSE; + } + + alGenBuffers(1, &_buffers[soundId]); + if((lastErrorCode = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: error regenerating buffer: %x\n", lastErrorCode); + _bufferStates[soundId] = CD_BS_FAILED; + return FALSE; + } else { + //We now have an empty buffer + _bufferStates[soundId] = CD_BS_EMPTY; + CCLOG(@"Denshion: 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]; //This is going to leak? + [opQ addOperation:loaderOp]; + } +} + + +/** + * 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 +{ + + ALenum format; + ALvoid* data; + ALsizei size; + ALsizei freq; + + CCLOG(@"Denshion: Loading openAL buffer %i %@", soundId, filePath); + +#ifdef DEBUG + //Sanity check parameters - only in DEBUG + NSAssert(soundId >= 0, @"soundId can not be negative"); + NSAssert(soundId < CD_MAX_BUFFERS, @"soundId exceeds limit set by CD_MAX_BUFFERS"); +#endif + + if (!functioning) { + //OpenAL initialisation has previously failed + CCLOG(@"Denshion: Loading buffer failed because sound engine state != functioning"); + return FALSE; + } + + CFURLRef fileURL = nil; + NSString *path = [FileUtils fullPathFromRelativePath:filePath]; + if (path) { + fileURL = (CFURLRef)[[NSURL fileURLWithPath:path] retain]; + } + + if (fileURL) + { + if (_bufferStates[soundId] != CD_BS_EMPTY) { + CCLOG(@"Denshion: non empty buffer, regenerating"); + if (![self unloadBuffer:soundId]) { + //Deletion of buffer failed, delete buffer routine has set buffer state and lastErrorCode + return FALSE; + } + } + + data = MyGetOpenALAudioData(fileURL, &size, &format, &freq); + CCLOG(@"Denshion: size %i frequency %i format %i %i", size, freq, format, data); + CFRelease(fileURL); + + if(data == NULL || (lastErrorCode = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: error loading sound: %x\n", lastErrorCode); + _bufferStates[soundId] = CD_BS_FAILED; + if (data != NULL) { + free(data);//Free memory, it was an OpenAL error so there is data to free + } + return FALSE; + } + + alBufferData(_buffers[soundId], format, data, size, freq); + free(data); + if((lastErrorCode = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: error attaching audio to buffer: %x\n", lastErrorCode); + _bufferStates[soundId] = CD_BS_FAILED; + return FALSE; + } + } else { + CCLOG(@"Denshion: Could not find file!\n"); + //Don't change buffer state here as it will be the same as before method was called + return FALSE; + } + + _bufferStates[soundId] = CD_BS_LOADED; + return TRUE; +} + +- (ALfloat) masterGain { + ALfloat gain; + alGetListenerf(AL_GAIN, &gain); + return gain; +} + +/** + * Overall gain setting multiplier. e.g 0.5 is half the gain. + */ +- (void) setMasterGain:(ALfloat) newGainValue { + alListenerf(AL_GAIN, newGainValue); +} + +- (BOOL) mute { + return _mute; +} + +/** + * Setting mute to true stops all sounds and prevent further sounds being played. If you do not want sounds to be stopped but just silenced + * then set masterGain to 0 instead. + */ +- (void) setMute:(BOOL) newMuteValue { + _mute = newMuteValue; + if (_mute) { + //Turn off all sounds + for (int i=0; i < _channelGroupTotal; i++) { + [self stopChannelGroup:i]; + } + } +} + +/** + * Play a sound. + * @param soundId the id of the sound to play (buffer id). + * @param channelGroupId the channel 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 channelGroupId:(int)channelGroupId pitch:(float) pitch pan:(float) pan gain:(float) gain loop:(BOOL) loop { + +#ifdef DEBUG + //Sanity check parameters - only in DEBUG + NSAssert(soundId >= 0, @"soundId can not be negative"); + NSAssert(soundId < CD_MAX_BUFFERS, @"soundId exceeds limit"); + NSAssert(channelGroupId >= 0, @"channelGroupId can not be negative"); + NSAssert(channelGroupId < _channelGroupTotal, @"channelGroupId 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 (_mute || !functioning || _bufferStates[soundId] != CD_BS_LOADED || _channelGroups[channelGroupId].mute) { +#ifdef DEBUG + if (!functioning) { + CCLOG(@"Denshion: sound playback aborted because sound engine is not functioning"); + } else if (_bufferStates[soundId] != CD_BS_LOADED) { + CCLOG(@"Denshion: sound playback aborted because buffer %i is not loaded", soundId); + } +#endif + return CD_MUTE; + } + + + //Work out which channel we can use + int channel = _channelGroups[channelGroupId].currentIndex; + if (channel != CD_CHANNEL_GROUP_NON_INTERRUPTIBLE) { + if (_channelGroups[channelGroupId].startIndex != _channelGroups[channelGroupId].endIndex) { + _channelGroups[channelGroupId].currentIndex++; + if(_channelGroups[channelGroupId].currentIndex > _channelGroups[channelGroupId].endIndex) { + _channelGroups[channelGroupId].currentIndex = _channelGroups[channelGroupId].startIndex; + } + } + return [self _startSound:soundId channelId:channel pitchVal:pitch panVal:pan gainVal:gain looping:loop checkState:TRUE]; + } else { + //Channel group is non interruptible therefore we must search for the first non playing channel/source if there are any + int checkingIndex = _channelGroups[channelGroupId].startIndex; + ALint state = 0; + while ((checkingIndex <= _channelGroups[channelGroupId].endIndex) && (channel == CD_CHANNEL_GROUP_NON_INTERRUPTIBLE)) { + //Check if source is playing + alGetSourcei(_sources[checkingIndex], AL_SOURCE_STATE, &state); + if (state != AL_PLAYING) { + channel = checkingIndex; + } + checkingIndex++; + } + + if (channel != CD_CHANNEL_GROUP_NON_INTERRUPTIBLE) { + //Found a free channel + return [self _startSound:soundId channelId:channel pitchVal:pitch panVal:pan gainVal:gain looping:loop checkState:FALSE]; + } else { + //Didn't find a free channel + return CD_NO_SOURCE; + } + } +} + +/** + * Internal method - use playSound instead. + */ +- (ALuint)_startSound:(int) soundId channelId:(int) channelId pitchVal:(float) pitchVal panVal:(float) panVal gainVal:(float) gainVal looping:(BOOL) looping checkState:(BOOL) checkState +{ + + ALint state; + ALuint source = _sources[channelId]; + ALuint buffer = _buffers[soundId]; + + alGetError();//Clear the error code + + //If we are in interruptible mode then we check the state to see if the source + //is already playing and if so stop it. Otherwise in non interruptible mode + //we already know that the source is not playing. + if (checkState) { + alGetSourcei(source, AL_SOURCE_STATE, &state); + if (state == AL_PLAYING) { + alSourceStop(source); + } + } + + alSourcei(source, AL_BUFFER, buffer);//Attach to sound + alSourcef(source, AL_PITCH, pitchVal);//Set pitch + alSourcei(source, AL_LOOPING, looping);//Set looping + alSourcef(source, AL_GAIN, gainVal);//Set gain/volume + float sourcePosAL[] = {panVal, 0.0f, 0.0f};//Set position - just using left and right panning + alSourcefv(source, AL_POSITION, sourcePosAL); + + alSourcePlay(source); + if((lastErrorCode = alGetError()) == AL_NO_ERROR) { + //Everything was okay + _sourceBufferAttachments[channelId] = buffer;//Keep track of which buffer source is attached to as alGetSourcei on AL_BUFFER does not seem to work + return source; + } else { + //Something went wrong - set error code and return failure code + return CD_NO_SOURCE; + } +} + +/** + * Stop all sounds playing within a channel group + */ +- (void) stopChannelGroup:(int) channelGroupId { + if (!functioning) { + return; + } + for (int i=_channelGroups[channelGroupId].startIndex; i <= _channelGroups[channelGroupId].endIndex; i++) { + alSourceStop(_sources[i]); + } +} + +/** + * Stop a sound playing. + * @param sourceId an OpenGL source identifier i.e. the return value of playSound + */ +- (void)stopSound:(ALuint) sourceId { + if (!functioning) { + return; + } + alSourceStop(sourceId); +} + +/** + * Set a channel group as non interruptible. Default is that channel groups are interruptible. + * Non interruptible means that if a request to play a sound is made for a channel group and there are + * no free channels available then the play request will be ignored and CD_NO_SOURCE will be returned. + */ +- (void) setChannelGroupNonInterruptible:(int) channelGroupId isNonInterruptible:(BOOL) isNonInterruptible { + if (isNonInterruptible) { + _channelGroups[channelGroupId].currentIndex = CD_CHANNEL_GROUP_NON_INTERRUPTIBLE; + } else { + _channelGroups[channelGroupId].currentIndex = _channelGroups[channelGroupId].startIndex; + } +} + +/** + * Set the mute property for a channel group. If mute is turned on any sounds in that channel group + * will be stopped and further sounds in that channel 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 channel group mute setting is. + */ +- (void) setChannelGroupMute:(int) channelGroupId mute:(BOOL) mute { + if (mute) { + _channelGroups[channelGroupId].mute = true; + [self stopChannelGroup:channelGroupId]; + } else { + _channelGroups[channelGroupId].mute = false; + } +} + +/** + * Return the mute property for the channel group identified by channelGroupId + */ +- (BOOL) channelGroupMute:(int) channelGroupId { + if (_channelGroups[channelGroupId].mute) { + return YES; + } else { + return NO; + } +} + +-(ALCcontext *) openALContext { + return context; +} + +//Code to handle audio session interruption. Thanks to Andy Fitter and Ben Britten. +-(void)audioSessionInterrupted +{ + CCLOG(@"Denshion: Audio session interrupted"); + ALenum error = AL_NO_ERROR; + // Deactivate the current audio session + AudioSessionSetActive(NO); + // set the current context to NULL will 'shutdown' openAL + alcMakeContextCurrent(NULL); + if((error = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: Error making context current %x\n", error); + } + // now suspend your context to 'pause' your sound world + alcSuspendContext(context); + if((error = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: Error suspending context %x\n", error); + } +} + +//Code to handle audio session resumption. Thanks to Andy Fitter and Ben Britten. +-(void)audioSessionResumed +{ + ALenum error = AL_NO_ERROR; + CCLOG(@"Denshion: Audio session resumed"); + // Reset audio session + OSStatus result = AudioSessionSetProperty ( kAudioSessionProperty_AudioCategory, sizeof(_audioSessionCategory), &_audioSessionCategory ); + + // Reactivate the current audio session + result = AudioSessionSetActive(YES); + + // Restore open al context + alcMakeContextCurrent(context); + if((error = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: Error making context current%x\n", error); + } + // 'unpause' my context + alcProcessContext(context); + if((error = alGetError()) != AL_NO_ERROR) { + CCLOG(@"Denshion: Error processing context%x\n", error); + } +} + +@end + +/////////////////////////////////////////////////////////////////////////////////////// + +@implementation CDSourceWrapper + +-(void) setSourceId:(ALuint) newSourceId { + if ((newSourceId != CD_NO_SOURCE) && (newSourceId != CD_MUTE)) { + sourceId = newSourceId; + } else { + CCLOG(@"Denshion: Attempt to assign CD_MUTE or CD_NO_SOURCE to a source wrapper"); + } +} + +- (ALuint) sourceId { + return sourceId; +} + +- (void) setPitch:(float) newPitchValue { + lastPitch = newPitchValue; + alSourcef(sourceId, AL_PITCH, newPitchValue); +} + +- (void) setGain:(float) newGainValue { + lastGain = newGainValue; + alSourcef(sourceId, AL_GAIN, newGainValue); +} + +- (void) setPan:(float) newPanValue { + lastPan = newPanValue; + float sourcePosAL[] = {newPanValue, 0.0f, 0.0f};//Set position - just using left and right panning + alSourcefv(sourceId, AL_POSITION, sourcePosAL); +} + +- (void) setLooping:(BOOL) newLoopingValue { + lastLooping = newLoopingValue; + alSourcei(sourceId, AL_LOOPING, newLoopingValue); +} + + +- (BOOL) isPlaying { + ALint state; + alGetSourcei(sourceId, AL_SOURCE_STATE, &state); + return (state == AL_PLAYING); +} + +//alGetSource does not appear to work for pitch, pan and gain values +//So we just remember the last value set +- (float) pitch { + /* + //This does not work on simulator or device + ALfloat pitchVal; + alGetSourcef(sourceId, AL_PITCH, &pitchVal); + return pitchVal; + */ + return lastPitch; +} + +- (float) pan { + return lastPan; +} + +- (float) gain { + return lastGain; +} + +- (BOOL) looping { + return lastLooping; +} + +@end + +//////////////////////////////////////////////////////////////////////////// + +@implementation CDAsynchBufferLoader + +-(id) init:(NSArray *)loadRequests soundEngine:(CDSoundEngine *) theSoundEngine { + if ([super init] ) { + _loadRequests = loadRequests; + [_loadRequests retain]; + _soundEngine = theSoundEngine; + [_soundEngine retain]; + return self; + } else { + return nil; + } +} + +-(void) main { + CCLOG(@"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; + +} + +-(void) dealloc { + [_loadRequests release]; + [_soundEngine release]; + [super dealloc]; +} + +@end + + +/////////////////////////////////////////////////////////////////////////////////////// +@implementation CDBufferLoadRequest + +@synthesize filePath, soundId; + +-(id) init:(int) theSoundId filePath:(NSString *) theFilePath { + if ([super init]) { + soundId = theSoundId; + filePath = theFilePath; + [filePath retain]; + return self; + } else { + return nil; + } +} + +-(void) dealloc { + [filePath release]; + [super dealloc]; +} + +@end diff --git a/CocosDenshion/SimpleAudioEngine.h b/CocosDenshion/SimpleAudioEngine.h new file mode 100644 index 0000000..3fc9aa3 --- /dev/null +++ b/CocosDenshion/SimpleAudioEngine.h @@ -0,0 +1,67 @@ +/* + * SimpleAudioEngine.h + * SweetDreams + * + * Created by João Caxaria on 5/24/09. + * Copyright 2009 Cocos2d-iPhone - If you find this useful, please give something back. + * Original by skeeet. + * http://groups.google.com/group/cocos2d-iphone-discuss/browse_thread/thread/166c5c488b55a858/98c606d518033637?lnk=gst&q=AVAudioPlayer&pli=1 + */ +#import "CocosDenshion.h" +#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 muted_; + +} + +/** whether or not the engine is muted */ +@property (readwrite) BOOL muted; +/** Background music volume. Range is 0.0f to 1.0f */ +@property (readwrite) float backgroundMusicVolume; +/** Effects volume. Range is 0.0f to 1.0f */ +@property (readwrite) float effectsVolume; + +/** returns the shared instance of the SimpleAudioEngine object */ ++ (SimpleAudioEngine*) sharedEngine; + +/** 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; + +@end diff --git a/CocosDenshion/SimpleAudioEngine.m b/CocosDenshion/SimpleAudioEngine.m new file mode 100644 index 0000000..14eb98a --- /dev/null +++ b/CocosDenshion/SimpleAudioEngine.m @@ -0,0 +1,247 @@ +/* + * SimpleAudioEngine.mm + * SweetDreams + * + * Created by João Caxaria on 5/24/09. + * Copyright 2009 Cocos2d-iPhone - If you find this useful, please give something back. + * + */ + +#import "SimpleAudioEngine.h" +#import + + +@interface SimpleAudioEngine (Buffers) + +-(NSNumber*) getNextAvailableBuffer; +-(void) freeBuffer:(NSNumber*) buffer; + +@end + + +@implementation SimpleAudioEngine + +static SimpleAudioEngine *sharedEngine = nil; +static CDSoundEngine* soundEngine = nil; +static NSMutableDictionary* loadedEffects = nil; +static bool usedBuffers[CD_MAX_BUFFERS]; +static CDAudioManager *am = nil; + +// Init ++ (SimpleAudioEngine *) sharedEngine +{ + @synchronized(self) { + if (!sharedEngine) + [[SimpleAudioEngine alloc] init]; + return sharedEngine; + } + return nil; +} + ++ (id) alloc +{ + @synchronized(self) { + NSAssert(sharedEngine == nil, @"Attempted to allocate a second instance of a singleton."); + sharedEngine = [super alloc]; + return sharedEngine; + } + return nil; +} + +-(id) init +{ + if((self=[super init])) { + + int channelGroups[1]; + channelGroups[0] = CD_MAX_SOURCES - 1; + //Setting up the audio manager with this mode means that if the user is playing music when the app starts then + //background music will not be played. + am = [[CDAudioManager alloc] init:kAudioManagerFxPlusMusicIfNoOtherAudio channelGroupDefinitions:channelGroups channelGroupTotal:1]; + soundEngine = am.soundEngine; + loadedEffects = [[NSMutableDictionary alloc] initWithCapacity:CD_MAX_BUFFERS]; + + muted_ = NO; + } + return self; +} + +// Memory +- (void) dealloc +{ + [am release]; + am = nil; + + soundEngine = nil; + + [loadedEffects autorelease]; + loadedEffects = nil; + + [super dealloc]; +} + +#pragma mark SimpleAudioEngine - background music + +-(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]; +} + +#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 +{ + NSNumber* soundId = (NSNumber*)[loadedEffects objectForKey:filePath]; + + if(soundId == nil) + { +#ifdef ASSERT_DEBUG + @throw [[[NSException alloc] initWithName:@"SimpleAudioEngine::playEffect" + reason:filePath userInfo:nil] autorelease]; +#else + [self preloadEffect:filePath]; + soundId = (NSNumber*)[loadedEffects objectForKey:filePath];//Issue 465 - thanks myBuddyCJ +#endif + } + + return [soundEngine playSound:[soundId intValue] channelGroupId:0 pitch:pitch pan:pan gain:gain loop:false]; +} + +-(void) stopEffect:(ALuint) soundId { + [soundEngine stopSound:soundId]; +} + +-(void) preloadEffect:(NSString*) filePath +{ + NSNumber* soundId = (NSNumber*)[loadedEffects objectForKey:filePath]; + + if(soundId != nil) + { + #ifdef ASSERT_DEBUG + @throw [[[NSException alloc] initWithName:@"SimpleAudioEngine::preloadEffect" reason:filePath userInfo:nil] autorelease]; + #else + return; + #endif + } + + NSNumber* position = [self getNextAvailableBuffer]; + [loadedEffects setObject:position forKey:filePath]; + [soundEngine loadBuffer:[position intValue] filePath:filePath]; +} + +-(void) unloadEffect:(NSString*) filePath +{ + NSNumber* soundId = [loadedEffects objectForKey:filePath]; + if(soundId == nil) + { + #ifdef ASSERT_DEBUG + @throw [[[NSException alloc] initWithName:@"SimpleAudioEngine::unloadEffect" reason:filePath userInfo:nil] autorelease]; + #else + return; + #endif + } + [self freeBuffer:soundId]; + [loadedEffects removeObjectForKey:filePath]; + [soundEngine unloadBuffer:[soundId intValue]]; +} + +#pragma mark SimpleAudioEngine - Muted +-(BOOL) muted +{ + return muted_; +} + +-(void) setMuted:(BOOL)muted +{ + if( muted_ != muted ) { + muted_ = muted; + [soundEngine setMute:muted]; + if( muted ) { + [am pauseBackgroundMusic]; + } else { + [am resumeBackgroundMusic]; + } + } +} + +#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; +} + + +@end + +#pragma mark SimpleAudioEngine - Buffers + +@implementation SimpleAudioEngine (Buffers) + +-(NSNumber*) getNextAvailableBuffer +{ + for(int i = 0; i < CD_MAX_BUFFERS ; i++) + { + if(!usedBuffers[i]) + { + usedBuffers[i] = true; + return [[[NSNumber alloc] initWithInt:i] autorelease]; + } + } +#ifdef ASSERT_DEBUG + @throw [[[NSException alloc] initWithName:@"AudioEngine::getNextAvailableBuffer" reason:@"Full buffers" userInfo:nil] autorelease]; +#endif + return nil;//Added to get rid of compiler warning +} + +-(void) freeBuffer:(NSNumber*) buffer +{ + usedBuffers[[buffer intValue]] = false; +} + +@end diff --git a/Default.png b/Default.png new file mode 100755 index 0000000000000000000000000000000000000000..8710d78ac8a00a3ea05bc35c3a3a1d568001a5a8 GIT binary patch literal 30635 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 literal 0 HcmV?d00001 diff --git a/Icon.png b/Icon.png new file mode 100755 index 0000000000000000000000000000000000000000..d187aa9fdf7a561e28e2bb0c43be0ddc6ecb8aa0 GIT binary patch literal 9327 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 + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.yourcompany.${PRODUCT_NAME:identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleVersion + 2.0 + LSRequiresIPhoneOS + + UIStatusBarHidden + + + diff --git a/Tetris.xcodeproj/project.pbxproj b/Tetris.xcodeproj/project.pbxproj new file mode 100755 index 0000000..f7d3c4c --- /dev/null +++ b/Tetris.xcodeproj/project.pbxproj @@ -0,0 +1,1119 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; }; + 1F3B9A2D0EF2145700286867 /* TetrisAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F3B9A2B0EF2145700286867 /* TetrisAppDelegate.m */; }; + 37BC6F76106EE4D200EF5C76 /* GameScene.m in Sources */ = {isa = PBXBuildFile; fileRef = 37BC6F75106EE4D200EF5C76 /* GameScene.m */; }; + 37BC6F8B106EE72300EF5C76 /* background.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 37BC6F8A106EE72300EF5C76 /* background.jpg */; }; + 37BC6FAA106EEC8000EF5C76 /* Block.m in Sources */ = {isa = PBXBuildFile; fileRef = 37BC6FA9106EEC8000EF5C76 /* Block.m */; }; + 37BC6FE9106F03D900EF5C76 /* GameLogicLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 37BC6FE8106F03D900EF5C76 /* GameLogicLayer.m */; }; + 37E45D5B1071507400B2E7D9 /* Tetromino.m in Sources */ = {isa = PBXBuildFile; fileRef = 37E45D5A1071507400B2E7D9 /* Tetromino.m */; }; + 37E45DC510715E1600B2E7D9 /* blue.png in Resources */ = {isa = PBXBuildFile; fileRef = 37E45DBE10715E1600B2E7D9 /* blue.png */; }; + 37E45DC610715E1600B2E7D9 /* cyan.png in Resources */ = {isa = PBXBuildFile; fileRef = 37E45DBF10715E1600B2E7D9 /* cyan.png */; }; + 37E45DC710715E1600B2E7D9 /* green.png in Resources */ = {isa = PBXBuildFile; fileRef = 37E45DC010715E1600B2E7D9 /* green.png */; }; + 37E45DC810715E1600B2E7D9 /* magenta.png in Resources */ = {isa = PBXBuildFile; fileRef = 37E45DC110715E1600B2E7D9 /* magenta.png */; }; + 37E45DC910715E1600B2E7D9 /* orange.png in Resources */ = {isa = PBXBuildFile; fileRef = 37E45DC210715E1600B2E7D9 /* orange.png */; }; + 37E45DCA10715E1600B2E7D9 /* red.png in Resources */ = {isa = PBXBuildFile; fileRef = 37E45DC310715E1600B2E7D9 /* red.png */; }; + 37E45DCB10715E1600B2E7D9 /* yellow.png in Resources */ = {isa = PBXBuildFile; fileRef = 37E45DC410715E1600B2E7D9 /* yellow.png */; }; + 37E45F6410717B9500B2E7D9 /* gameover.jpeg in Resources */ = {isa = PBXBuildFile; fileRef = 37E45F6310717B9500B2E7D9 /* gameover.jpeg */; }; + 505573C01045D1BE00A31725 /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 505573BF1045D1BE00A31725 /* Icon.png */; }; + 505573C21045D1EA00A31725 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 505573C11045D1EA00A31725 /* Default.png */; }; + 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 */; }; + 506EDB88102F4C4000A389B3 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 506EDB87102F4C4000A389B3 /* libz.dylib */; }; + 506EDBA5102F4C9F00A389B3 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 506EDBA4102F4C9F00A389B3 /* AVFoundation.framework */; }; + 506EE10010304F7500A389B3 /* Action.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE06710304F7500A389B3 /* Action.h */; }; + 506EE10110304F7500A389B3 /* Action.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE06810304F7500A389B3 /* Action.m */; }; + 506EE10210304F7500A389B3 /* ActionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE06910304F7500A389B3 /* ActionManager.h */; }; + 506EE10310304F7500A389B3 /* ActionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE06A10304F7500A389B3 /* ActionManager.m */; }; + 506EE10410304F7500A389B3 /* AtlasNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE06B10304F7500A389B3 /* AtlasNode.h */; }; + 506EE10510304F7500A389B3 /* AtlasNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE06C10304F7500A389B3 /* AtlasNode.m */; }; + 506EE10610304F7500A389B3 /* AtlasSprite.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE06D10304F7500A389B3 /* AtlasSprite.h */; }; + 506EE10710304F7500A389B3 /* AtlasSprite.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE06E10304F7500A389B3 /* AtlasSprite.m */; }; + 506EE10810304F7500A389B3 /* AtlasSpriteManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE06F10304F7500A389B3 /* AtlasSpriteManager.h */; }; + 506EE10910304F7500A389B3 /* AtlasSpriteManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE07010304F7500A389B3 /* AtlasSpriteManager.m */; }; + 506EE10A10304F7500A389B3 /* BitmapFontAtlas.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE07110304F7500A389B3 /* BitmapFontAtlas.h */; }; + 506EE10B10304F7500A389B3 /* BitmapFontAtlas.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE07210304F7500A389B3 /* BitmapFontAtlas.m */; }; + 506EE10C10304F7500A389B3 /* Camera.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE07310304F7500A389B3 /* Camera.h */; }; + 506EE10D10304F7500A389B3 /* Camera.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE07410304F7500A389B3 /* Camera.m */; }; + 506EE10E10304F7500A389B3 /* CameraAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE07510304F7500A389B3 /* CameraAction.h */; }; + 506EE10F10304F7500A389B3 /* CameraAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE07610304F7500A389B3 /* CameraAction.m */; }; + 506EE11010304F7500A389B3 /* ccExceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE07710304F7500A389B3 /* ccExceptions.h */; }; + 506EE11110304F7500A389B3 /* ccMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE07810304F7500A389B3 /* ccMacros.h */; }; + 506EE11210304F7500A389B3 /* ccTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE07910304F7500A389B3 /* ccTypes.h */; }; + 506EE11310304F7500A389B3 /* cocos2d.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE07A10304F7500A389B3 /* cocos2d.h */; }; + 506EE11410304F7500A389B3 /* cocos2d.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE07B10304F7500A389B3 /* cocos2d.m */; }; + 506EE11510304F7500A389B3 /* CocosNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE07C10304F7500A389B3 /* CocosNode.h */; }; + 506EE11610304F7500A389B3 /* CocosNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE07D10304F7500A389B3 /* CocosNode.m */; }; + 506EE11710304F7500A389B3 /* Director.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE07E10304F7500A389B3 /* Director.h */; }; + 506EE11810304F7500A389B3 /* Director.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE07F10304F7500A389B3 /* Director.m */; }; + 506EE11910304F7500A389B3 /* DrawingPrimitives.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE08010304F7500A389B3 /* DrawingPrimitives.h */; }; + 506EE11A10304F7500A389B3 /* DrawingPrimitives.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE08110304F7500A389B3 /* DrawingPrimitives.m */; }; + 506EE11B10304F7500A389B3 /* EaseAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE08210304F7500A389B3 /* EaseAction.h */; }; + 506EE11C10304F7500A389B3 /* EaseAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE08310304F7500A389B3 /* EaseAction.m */; }; + 506EE11D10304F7500A389B3 /* Grabber.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE08410304F7500A389B3 /* Grabber.h */; }; + 506EE11E10304F7500A389B3 /* Grabber.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE08510304F7500A389B3 /* Grabber.m */; }; + 506EE11F10304F7500A389B3 /* Grid.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE08610304F7500A389B3 /* Grid.h */; }; + 506EE12010304F7500A389B3 /* Grid.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE08710304F7500A389B3 /* Grid.m */; }; + 506EE12110304F7500A389B3 /* Grid3DAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE08810304F7500A389B3 /* Grid3DAction.h */; }; + 506EE12210304F7500A389B3 /* Grid3DAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE08910304F7500A389B3 /* Grid3DAction.m */; }; + 506EE12310304F7500A389B3 /* GridAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE08A10304F7500A389B3 /* GridAction.h */; }; + 506EE12410304F7500A389B3 /* GridAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE08B10304F7500A389B3 /* GridAction.m */; }; + 506EE12510304F7500A389B3 /* InstantAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE08C10304F7500A389B3 /* InstantAction.h */; }; + 506EE12610304F7500A389B3 /* InstantAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE08D10304F7500A389B3 /* InstantAction.m */; }; + 506EE12710304F7500A389B3 /* IntervalAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE08E10304F7500A389B3 /* IntervalAction.h */; }; + 506EE12810304F7500A389B3 /* IntervalAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE08F10304F7500A389B3 /* IntervalAction.m */; }; + 506EE12910304F7500A389B3 /* Label.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE09010304F7500A389B3 /* Label.h */; }; + 506EE12A10304F7500A389B3 /* Label.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE09110304F7500A389B3 /* Label.m */; }; + 506EE12B10304F7500A389B3 /* LabelAtlas.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE09210304F7500A389B3 /* LabelAtlas.h */; }; + 506EE12C10304F7500A389B3 /* LabelAtlas.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE09310304F7500A389B3 /* LabelAtlas.m */; }; + 506EE12D10304F7500A389B3 /* Layer.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE09410304F7500A389B3 /* Layer.h */; }; + 506EE12E10304F7500A389B3 /* Layer.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE09510304F7500A389B3 /* Layer.m */; }; + 506EE12F10304F7500A389B3 /* Menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE09610304F7500A389B3 /* Menu.h */; }; + 506EE13010304F7500A389B3 /* Menu.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE09710304F7500A389B3 /* Menu.m */; }; + 506EE13110304F7500A389B3 /* MenuItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE09810304F7500A389B3 /* MenuItem.h */; }; + 506EE13210304F7500A389B3 /* MenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE09910304F7500A389B3 /* MenuItem.m */; }; + 506EE13310304F7500A389B3 /* MotionStreak.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE09A10304F7500A389B3 /* MotionStreak.h */; }; + 506EE13410304F7500A389B3 /* MotionStreak.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE09B10304F7500A389B3 /* MotionStreak.m */; }; + 506EE13510304F7500A389B3 /* ParallaxNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE09C10304F7500A389B3 /* ParallaxNode.h */; }; + 506EE13610304F7500A389B3 /* ParallaxNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE09D10304F7500A389B3 /* ParallaxNode.m */; }; + 506EE13710304F7500A389B3 /* ParticleExamples.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE09E10304F7500A389B3 /* ParticleExamples.h */; }; + 506EE13810304F7500A389B3 /* ParticleExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE09F10304F7500A389B3 /* ParticleExamples.m */; }; + 506EE13910304F7500A389B3 /* ParticleSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0A010304F7500A389B3 /* ParticleSystem.h */; }; + 506EE13A10304F7500A389B3 /* ParticleSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0A110304F7500A389B3 /* ParticleSystem.m */; }; + 506EE13B10304F7500A389B3 /* PointParticleSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0A210304F7500A389B3 /* PointParticleSystem.h */; }; + 506EE13C10304F7500A389B3 /* PointParticleSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0A310304F7500A389B3 /* PointParticleSystem.m */; }; + 506EE13D10304F7500A389B3 /* QuadParticleSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0A410304F7500A389B3 /* QuadParticleSystem.h */; }; + 506EE13E10304F7500A389B3 /* QuadParticleSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0A510304F7500A389B3 /* QuadParticleSystem.m */; }; + 506EE13F10304F7500A389B3 /* RenderTexture.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0A610304F7500A389B3 /* RenderTexture.h */; }; + 506EE14010304F7500A389B3 /* RenderTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0A710304F7500A389B3 /* RenderTexture.m */; }; + 506EE14110304F7500A389B3 /* Ribbon.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0A810304F7500A389B3 /* Ribbon.h */; }; + 506EE14210304F7500A389B3 /* Ribbon.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0A910304F7500A389B3 /* Ribbon.m */; }; + 506EE14310304F7500A389B3 /* Scene.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0AA10304F7500A389B3 /* Scene.h */; }; + 506EE14410304F7500A389B3 /* Scene.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0AB10304F7500A389B3 /* Scene.m */; }; + 506EE14510304F7500A389B3 /* Scheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0AC10304F7500A389B3 /* Scheduler.h */; }; + 506EE14610304F7500A389B3 /* Scheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0AD10304F7500A389B3 /* Scheduler.m */; }; + 506EE14710304F7500A389B3 /* Sprite.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0AE10304F7500A389B3 /* Sprite.h */; }; + 506EE14810304F7500A389B3 /* Sprite.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0AF10304F7500A389B3 /* Sprite.m */; }; + 506EE14910304F7500A389B3 /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0B110304F7500A389B3 /* base64.c */; }; + 506EE14A10304F7500A389B3 /* base64.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0B210304F7500A389B3 /* base64.h */; }; + 506EE14B10304F7500A389B3 /* ccArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0B310304F7500A389B3 /* ccArray.h */; }; + 506EE14C10304F7500A389B3 /* ccHashSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0B410304F7500A389B3 /* ccHashSet.h */; }; + 506EE14D10304F7500A389B3 /* ccHashSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0B510304F7500A389B3 /* ccHashSet.m */; }; + 506EE14E10304F7500A389B3 /* CGPointExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0B610304F7500A389B3 /* CGPointExtension.h */; }; + 506EE14F10304F7500A389B3 /* CGPointExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0B710304F7500A389B3 /* CGPointExtension.m */; }; + 506EE15010304F7500A389B3 /* EAGLView.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0B810304F7500A389B3 /* EAGLView.h */; }; + 506EE15110304F7500A389B3 /* EAGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0B910304F7500A389B3 /* EAGLView.m */; }; + 506EE15210304F7500A389B3 /* FileUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0BA10304F7500A389B3 /* FileUtils.h */; }; + 506EE15310304F7500A389B3 /* FileUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0BB10304F7500A389B3 /* FileUtils.m */; }; + 506EE15410304F7500A389B3 /* glu.c in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0BC10304F7500A389B3 /* glu.c */; }; + 506EE15510304F7500A389B3 /* glu.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0BD10304F7500A389B3 /* glu.h */; }; + 506EE15610304F7500A389B3 /* OpenGL_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0BE10304F7500A389B3 /* OpenGL_Internal.h */; }; + 506EE15710304F7500A389B3 /* PVRTexture.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0BF10304F7500A389B3 /* PVRTexture.h */; }; + 506EE15810304F7500A389B3 /* PVRTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0C010304F7500A389B3 /* PVRTexture.m */; }; + 506EE15910304F7500A389B3 /* Texture2D.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0C110304F7500A389B3 /* Texture2D.h */; }; + 506EE15A10304F7500A389B3 /* Texture2D.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0C210304F7500A389B3 /* Texture2D.m */; }; + 506EE15B10304F7500A389B3 /* TGAlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0C310304F7500A389B3 /* TGAlib.h */; }; + 506EE15C10304F7500A389B3 /* TGAlib.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0C410304F7500A389B3 /* TGAlib.m */; }; + 506EE15D10304F7500A389B3 /* TransformUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0C510304F7500A389B3 /* TransformUtils.h */; }; + 506EE15E10304F7500A389B3 /* TransformUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0C610304F7500A389B3 /* TransformUtils.m */; }; + 506EE15F10304F7500A389B3 /* ZipUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0C710304F7500A389B3 /* ZipUtils.h */; }; + 506EE16010304F7500A389B3 /* ZipUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0C810304F7500A389B3 /* ZipUtils.m */; }; + 506EE16110304F7500A389B3 /* TextureAtlas.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0C910304F7500A389B3 /* TextureAtlas.h */; }; + 506EE16210304F7500A389B3 /* TextureAtlas.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0CA10304F7500A389B3 /* TextureAtlas.m */; }; + 506EE16310304F7500A389B3 /* TextureMgr.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0CB10304F7500A389B3 /* TextureMgr.h */; }; + 506EE16410304F7500A389B3 /* TextureMgr.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0CC10304F7500A389B3 /* TextureMgr.m */; }; + 506EE16510304F7500A389B3 /* TextureNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0CD10304F7500A389B3 /* TextureNode.h */; }; + 506EE16610304F7500A389B3 /* TextureNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0CE10304F7500A389B3 /* TextureNode.m */; }; + 506EE16710304F7500A389B3 /* TiledGridAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0CF10304F7500A389B3 /* TiledGridAction.h */; }; + 506EE16810304F7500A389B3 /* TiledGridAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0D010304F7500A389B3 /* TiledGridAction.m */; }; + 506EE16910304F7500A389B3 /* TileMapAtlas.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0D110304F7500A389B3 /* TileMapAtlas.h */; }; + 506EE16A10304F7500A389B3 /* TileMapAtlas.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0D210304F7500A389B3 /* TileMapAtlas.m */; }; + 506EE16B10304F7500A389B3 /* TMXTiledMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0D310304F7500A389B3 /* TMXTiledMap.h */; }; + 506EE16C10304F7500A389B3 /* TMXTiledMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0D410304F7500A389B3 /* TMXTiledMap.m */; }; + 506EE16D10304F7500A389B3 /* TMXXMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0D510304F7500A389B3 /* TMXXMLParser.h */; }; + 506EE16E10304F7500A389B3 /* TMXXMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0D610304F7500A389B3 /* TMXXMLParser.m */; }; + 506EE16F10304F7500A389B3 /* TouchDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0D710304F7500A389B3 /* TouchDelegateProtocol.h */; }; + 506EE17010304F7500A389B3 /* TouchDispatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0D810304F7500A389B3 /* TouchDispatcher.h */; }; + 506EE17110304F7500A389B3 /* TouchDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0D910304F7500A389B3 /* TouchDispatcher.m */; }; + 506EE17210304F7500A389B3 /* TouchHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0DA10304F7500A389B3 /* TouchHandler.h */; }; + 506EE17310304F7500A389B3 /* TouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0DB10304F7500A389B3 /* TouchHandler.m */; }; + 506EE17410304F7500A389B3 /* Transition.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0DC10304F7500A389B3 /* Transition.h */; }; + 506EE17510304F7500A389B3 /* Transition.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0DD10304F7500A389B3 /* Transition.m */; }; + 506EE17610304F7500A389B3 /* CDAudioManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0DF10304F7500A389B3 /* CDAudioManager.h */; }; + 506EE17710304F7500A389B3 /* CDAudioManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0E010304F7500A389B3 /* CDAudioManager.m */; }; + 506EE17810304F7500A389B3 /* CDOpenALSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0E110304F7500A389B3 /* CDOpenALSupport.h */; }; + 506EE17910304F7500A389B3 /* CocosDenshion.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0E210304F7500A389B3 /* CocosDenshion.h */; }; + 506EE17A10304F7500A389B3 /* CocosDenshion.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0E310304F7500A389B3 /* CocosDenshion.m */; }; + 506EE17B10304F7500A389B3 /* SimpleAudioEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0E410304F7500A389B3 /* SimpleAudioEngine.h */; }; + 506EE17C10304F7500A389B3 /* SimpleAudioEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0E510304F7500A389B3 /* SimpleAudioEngine.m */; }; + 506EE17D10304F7500A389B3 /* cocoslive.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0E710304F7500A389B3 /* cocoslive.h */; }; + 506EE17E10304F7500A389B3 /* cocoslive.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0E810304F7500A389B3 /* cocoslive.m */; }; + 506EE17F10304F7500A389B3 /* ScoreServerPost.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0E910304F7500A389B3 /* ScoreServerPost.h */; }; + 506EE18010304F7500A389B3 /* ScoreServerPost.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0EA10304F7500A389B3 /* ScoreServerPost.m */; }; + 506EE18110304F7500A389B3 /* ScoreServerRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0EB10304F7500A389B3 /* ScoreServerRequest.h */; }; + 506EE18210304F7500A389B3 /* ScoreServerRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0EC10304F7500A389B3 /* ScoreServerRequest.m */; }; + 506EE18310304F7500A389B3 /* CDataScanner.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0EE10304F7500A389B3 /* CDataScanner.h */; }; + 506EE18410304F7500A389B3 /* CDataScanner.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0EF10304F7500A389B3 /* CDataScanner.m */; }; + 506EE18510304F7500A389B3 /* CDataScanner_Extensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0F110304F7500A389B3 /* CDataScanner_Extensions.h */; }; + 506EE18610304F7500A389B3 /* CDataScanner_Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0F210304F7500A389B3 /* CDataScanner_Extensions.m */; }; + 506EE18710304F7500A389B3 /* NSCharacterSet_Extensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0F310304F7500A389B3 /* NSCharacterSet_Extensions.h */; }; + 506EE18810304F7500A389B3 /* NSCharacterSet_Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0F410304F7500A389B3 /* NSCharacterSet_Extensions.m */; }; + 506EE18910304F7500A389B3 /* NSDictionary_JSONExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0F510304F7500A389B3 /* NSDictionary_JSONExtensions.h */; }; + 506EE18A10304F7500A389B3 /* NSDictionary_JSONExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0F610304F7500A389B3 /* NSDictionary_JSONExtensions.m */; }; + 506EE18B10304F7500A389B3 /* NSScanner_Extensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0F710304F7500A389B3 /* NSScanner_Extensions.h */; }; + 506EE18C10304F7500A389B3 /* NSScanner_Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0F810304F7500A389B3 /* NSScanner_Extensions.m */; }; + 506EE18D10304F7500A389B3 /* CJSONDeserializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0FA10304F7500A389B3 /* CJSONDeserializer.h */; }; + 506EE18E10304F7500A389B3 /* CJSONDeserializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0FB10304F7500A389B3 /* CJSONDeserializer.m */; }; + 506EE18F10304F7500A389B3 /* CJSONScanner.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0FC10304F7500A389B3 /* CJSONScanner.h */; }; + 506EE19010304F7500A389B3 /* CJSONScanner.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0FD10304F7500A389B3 /* CJSONScanner.m */; }; + 506EE19110304F7500A389B3 /* CJSONSerializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 506EE0FE10304F7500A389B3 /* CJSONSerializer.h */; }; + 506EE19210304F7500A389B3 /* CJSONSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 506EE0FF10304F7500A389B3 /* CJSONSerializer.m */; }; + 506EE1A91030508200A389B3 /* libcocos2d libraries.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 506EE05E10304ED200A389B3 /* libcocos2d libraries.a */; }; + CA3EACB90FEE7024004BBF8E /* fps_images.png in Resources */ = {isa = PBXBuildFile; fileRef = CA3EACB80FEE7024004BBF8E /* fps_images.png */; }; + 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 */; }; +/* 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 /* Tetris.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Tetris.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1F3B9A2B0EF2145700286867 /* TetrisAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TetrisAppDelegate.m; path = Classes/TetrisAppDelegate.m; sourceTree = SOURCE_ROOT; }; + 1F3B9A2C0EF2145700286867 /* TetrisAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TetrisAppDelegate.h; path = Classes/TetrisAppDelegate.h; sourceTree = SOURCE_ROOT; }; + 1F3B9A820EF2151B00286867 /* Tetris_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tetris_Prefix.pch; sourceTree = SOURCE_ROOT; }; + 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 37BC6F74106EE4D200EF5C76 /* GameScene.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GameScene.h; sourceTree = ""; }; + 37BC6F75106EE4D200EF5C76 /* GameScene.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GameScene.m; sourceTree = ""; }; + 37BC6F8A106EE72300EF5C76 /* background.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = background.jpg; sourceTree = ""; }; + 37BC6FA8106EEC8000EF5C76 /* Block.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Block.h; sourceTree = ""; }; + 37BC6FA9106EEC8000EF5C76 /* Block.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Block.m; sourceTree = ""; }; + 37BC6FE7106F03D900EF5C76 /* GameLogicLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GameLogicLayer.h; sourceTree = ""; }; + 37BC6FE8106F03D900EF5C76 /* GameLogicLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GameLogicLayer.m; sourceTree = ""; }; + 37E45D591071507400B2E7D9 /* Tetromino.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tetromino.h; sourceTree = ""; }; + 37E45D5A1071507400B2E7D9 /* Tetromino.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Tetromino.m; sourceTree = ""; }; + 37E45DBE10715E1600B2E7D9 /* blue.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = blue.png; sourceTree = ""; }; + 37E45DBF10715E1600B2E7D9 /* cyan.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cyan.png; sourceTree = ""; }; + 37E45DC010715E1600B2E7D9 /* green.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = green.png; sourceTree = ""; }; + 37E45DC110715E1600B2E7D9 /* magenta.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = magenta.png; sourceTree = ""; }; + 37E45DC210715E1600B2E7D9 /* orange.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = orange.png; sourceTree = ""; }; + 37E45DC310715E1600B2E7D9 /* red.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = red.png; sourceTree = ""; }; + 37E45DC410715E1600B2E7D9 /* yellow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = yellow.png; sourceTree = ""; }; + 37E45F6310717B9500B2E7D9 /* gameover.jpeg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gameover.jpeg; sourceTree = ""; }; + 505573BF1045D1BE00A31725 /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Icon.png; sourceTree = ""; }; + 505573C11045D1EA00A31725 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; 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; }; + 506EE05E10304ED200A389B3 /* libcocos2d libraries.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libcocos2d libraries.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 506EE06710304F7500A389B3 /* Action.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Action.h; sourceTree = ""; }; + 506EE06810304F7500A389B3 /* Action.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Action.m; sourceTree = ""; }; + 506EE06910304F7500A389B3 /* ActionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ActionManager.h; sourceTree = ""; }; + 506EE06A10304F7500A389B3 /* ActionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ActionManager.m; sourceTree = ""; }; + 506EE06B10304F7500A389B3 /* AtlasNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AtlasNode.h; sourceTree = ""; }; + 506EE06C10304F7500A389B3 /* AtlasNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AtlasNode.m; sourceTree = ""; }; + 506EE06D10304F7500A389B3 /* AtlasSprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AtlasSprite.h; sourceTree = ""; }; + 506EE06E10304F7500A389B3 /* AtlasSprite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AtlasSprite.m; sourceTree = ""; }; + 506EE06F10304F7500A389B3 /* AtlasSpriteManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AtlasSpriteManager.h; sourceTree = ""; }; + 506EE07010304F7500A389B3 /* AtlasSpriteManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AtlasSpriteManager.m; sourceTree = ""; }; + 506EE07110304F7500A389B3 /* BitmapFontAtlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BitmapFontAtlas.h; sourceTree = ""; }; + 506EE07210304F7500A389B3 /* BitmapFontAtlas.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BitmapFontAtlas.m; sourceTree = ""; }; + 506EE07310304F7500A389B3 /* Camera.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Camera.h; sourceTree = ""; }; + 506EE07410304F7500A389B3 /* Camera.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Camera.m; sourceTree = ""; }; + 506EE07510304F7500A389B3 /* CameraAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CameraAction.h; sourceTree = ""; }; + 506EE07610304F7500A389B3 /* CameraAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraAction.m; sourceTree = ""; }; + 506EE07710304F7500A389B3 /* ccExceptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccExceptions.h; sourceTree = ""; }; + 506EE07810304F7500A389B3 /* ccMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccMacros.h; sourceTree = ""; }; + 506EE07910304F7500A389B3 /* ccTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccTypes.h; sourceTree = ""; }; + 506EE07A10304F7500A389B3 /* cocos2d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cocos2d.h; sourceTree = ""; }; + 506EE07B10304F7500A389B3 /* cocos2d.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = cocos2d.m; sourceTree = ""; }; + 506EE07C10304F7500A389B3 /* CocosNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CocosNode.h; sourceTree = ""; }; + 506EE07D10304F7500A389B3 /* CocosNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CocosNode.m; sourceTree = ""; }; + 506EE07E10304F7500A389B3 /* Director.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Director.h; sourceTree = ""; }; + 506EE07F10304F7500A389B3 /* Director.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Director.m; sourceTree = ""; }; + 506EE08010304F7500A389B3 /* DrawingPrimitives.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DrawingPrimitives.h; sourceTree = ""; }; + 506EE08110304F7500A389B3 /* DrawingPrimitives.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DrawingPrimitives.m; sourceTree = ""; }; + 506EE08210304F7500A389B3 /* EaseAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EaseAction.h; sourceTree = ""; }; + 506EE08310304F7500A389B3 /* EaseAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EaseAction.m; sourceTree = ""; }; + 506EE08410304F7500A389B3 /* Grabber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Grabber.h; sourceTree = ""; }; + 506EE08510304F7500A389B3 /* Grabber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Grabber.m; sourceTree = ""; }; + 506EE08610304F7500A389B3 /* Grid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Grid.h; sourceTree = ""; }; + 506EE08710304F7500A389B3 /* Grid.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Grid.m; sourceTree = ""; }; + 506EE08810304F7500A389B3 /* Grid3DAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Grid3DAction.h; sourceTree = ""; }; + 506EE08910304F7500A389B3 /* Grid3DAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Grid3DAction.m; sourceTree = ""; }; + 506EE08A10304F7500A389B3 /* GridAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GridAction.h; sourceTree = ""; }; + 506EE08B10304F7500A389B3 /* GridAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GridAction.m; sourceTree = ""; }; + 506EE08C10304F7500A389B3 /* InstantAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InstantAction.h; sourceTree = ""; }; + 506EE08D10304F7500A389B3 /* InstantAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InstantAction.m; sourceTree = ""; }; + 506EE08E10304F7500A389B3 /* IntervalAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntervalAction.h; sourceTree = ""; }; + 506EE08F10304F7500A389B3 /* IntervalAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IntervalAction.m; sourceTree = ""; }; + 506EE09010304F7500A389B3 /* Label.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Label.h; sourceTree = ""; }; + 506EE09110304F7500A389B3 /* Label.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Label.m; sourceTree = ""; }; + 506EE09210304F7500A389B3 /* LabelAtlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LabelAtlas.h; sourceTree = ""; }; + 506EE09310304F7500A389B3 /* LabelAtlas.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LabelAtlas.m; sourceTree = ""; }; + 506EE09410304F7500A389B3 /* Layer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Layer.h; sourceTree = ""; }; + 506EE09510304F7500A389B3 /* Layer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Layer.m; sourceTree = ""; }; + 506EE09610304F7500A389B3 /* Menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Menu.h; sourceTree = ""; }; + 506EE09710304F7500A389B3 /* Menu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Menu.m; sourceTree = ""; }; + 506EE09810304F7500A389B3 /* MenuItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MenuItem.h; sourceTree = ""; }; + 506EE09910304F7500A389B3 /* MenuItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MenuItem.m; sourceTree = ""; }; + 506EE09A10304F7500A389B3 /* MotionStreak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MotionStreak.h; sourceTree = ""; }; + 506EE09B10304F7500A389B3 /* MotionStreak.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MotionStreak.m; sourceTree = ""; }; + 506EE09C10304F7500A389B3 /* ParallaxNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParallaxNode.h; sourceTree = ""; }; + 506EE09D10304F7500A389B3 /* ParallaxNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ParallaxNode.m; sourceTree = ""; }; + 506EE09E10304F7500A389B3 /* ParticleExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParticleExamples.h; sourceTree = ""; }; + 506EE09F10304F7500A389B3 /* ParticleExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ParticleExamples.m; sourceTree = ""; }; + 506EE0A010304F7500A389B3 /* ParticleSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParticleSystem.h; sourceTree = ""; }; + 506EE0A110304F7500A389B3 /* ParticleSystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ParticleSystem.m; sourceTree = ""; }; + 506EE0A210304F7500A389B3 /* PointParticleSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PointParticleSystem.h; sourceTree = ""; }; + 506EE0A310304F7500A389B3 /* PointParticleSystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PointParticleSystem.m; sourceTree = ""; }; + 506EE0A410304F7500A389B3 /* QuadParticleSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QuadParticleSystem.h; sourceTree = ""; }; + 506EE0A510304F7500A389B3 /* QuadParticleSystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QuadParticleSystem.m; sourceTree = ""; }; + 506EE0A610304F7500A389B3 /* RenderTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderTexture.h; sourceTree = ""; }; + 506EE0A710304F7500A389B3 /* RenderTexture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RenderTexture.m; sourceTree = ""; }; + 506EE0A810304F7500A389B3 /* Ribbon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Ribbon.h; sourceTree = ""; }; + 506EE0A910304F7500A389B3 /* Ribbon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Ribbon.m; sourceTree = ""; }; + 506EE0AA10304F7500A389B3 /* Scene.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scene.h; sourceTree = ""; }; + 506EE0AB10304F7500A389B3 /* Scene.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Scene.m; sourceTree = ""; }; + 506EE0AC10304F7500A389B3 /* Scheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scheduler.h; sourceTree = ""; }; + 506EE0AD10304F7500A389B3 /* Scheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Scheduler.m; sourceTree = ""; }; + 506EE0AE10304F7500A389B3 /* Sprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Sprite.h; sourceTree = ""; }; + 506EE0AF10304F7500A389B3 /* Sprite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Sprite.m; sourceTree = ""; }; + 506EE0B110304F7500A389B3 /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = base64.c; sourceTree = ""; }; + 506EE0B210304F7500A389B3 /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base64.h; sourceTree = ""; }; + 506EE0B310304F7500A389B3 /* ccArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccArray.h; sourceTree = ""; }; + 506EE0B410304F7500A389B3 /* ccHashSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccHashSet.h; sourceTree = ""; }; + 506EE0B510304F7500A389B3 /* ccHashSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ccHashSet.m; sourceTree = ""; }; + 506EE0B610304F7500A389B3 /* CGPointExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CGPointExtension.h; sourceTree = ""; }; + 506EE0B710304F7500A389B3 /* CGPointExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CGPointExtension.m; sourceTree = ""; }; + 506EE0B810304F7500A389B3 /* EAGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EAGLView.h; sourceTree = ""; }; + 506EE0B910304F7500A389B3 /* EAGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EAGLView.m; sourceTree = ""; }; + 506EE0BA10304F7500A389B3 /* FileUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileUtils.h; sourceTree = ""; }; + 506EE0BB10304F7500A389B3 /* FileUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FileUtils.m; sourceTree = ""; }; + 506EE0BC10304F7500A389B3 /* glu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = glu.c; sourceTree = ""; }; + 506EE0BD10304F7500A389B3 /* glu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = glu.h; sourceTree = ""; }; + 506EE0BE10304F7500A389B3 /* OpenGL_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenGL_Internal.h; sourceTree = ""; }; + 506EE0BF10304F7500A389B3 /* PVRTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRTexture.h; sourceTree = ""; }; + 506EE0C010304F7500A389B3 /* PVRTexture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PVRTexture.m; sourceTree = ""; }; + 506EE0C110304F7500A389B3 /* Texture2D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Texture2D.h; sourceTree = ""; }; + 506EE0C210304F7500A389B3 /* Texture2D.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Texture2D.m; sourceTree = ""; }; + 506EE0C310304F7500A389B3 /* TGAlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGAlib.h; sourceTree = ""; }; + 506EE0C410304F7500A389B3 /* TGAlib.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGAlib.m; sourceTree = ""; }; + 506EE0C510304F7500A389B3 /* TransformUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TransformUtils.h; sourceTree = ""; }; + 506EE0C610304F7500A389B3 /* TransformUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TransformUtils.m; sourceTree = ""; }; + 506EE0C710304F7500A389B3 /* ZipUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZipUtils.h; sourceTree = ""; }; + 506EE0C810304F7500A389B3 /* ZipUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZipUtils.m; sourceTree = ""; }; + 506EE0C910304F7500A389B3 /* TextureAtlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextureAtlas.h; sourceTree = ""; }; + 506EE0CA10304F7500A389B3 /* TextureAtlas.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TextureAtlas.m; sourceTree = ""; }; + 506EE0CB10304F7500A389B3 /* TextureMgr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextureMgr.h; sourceTree = ""; }; + 506EE0CC10304F7500A389B3 /* TextureMgr.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TextureMgr.m; sourceTree = ""; }; + 506EE0CD10304F7500A389B3 /* TextureNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextureNode.h; sourceTree = ""; }; + 506EE0CE10304F7500A389B3 /* TextureNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TextureNode.m; sourceTree = ""; }; + 506EE0CF10304F7500A389B3 /* TiledGridAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TiledGridAction.h; sourceTree = ""; }; + 506EE0D010304F7500A389B3 /* TiledGridAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TiledGridAction.m; sourceTree = ""; }; + 506EE0D110304F7500A389B3 /* TileMapAtlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TileMapAtlas.h; sourceTree = ""; }; + 506EE0D210304F7500A389B3 /* TileMapAtlas.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TileMapAtlas.m; sourceTree = ""; }; + 506EE0D310304F7500A389B3 /* TMXTiledMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TMXTiledMap.h; sourceTree = ""; }; + 506EE0D410304F7500A389B3 /* TMXTiledMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TMXTiledMap.m; sourceTree = ""; }; + 506EE0D510304F7500A389B3 /* TMXXMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TMXXMLParser.h; sourceTree = ""; }; + 506EE0D610304F7500A389B3 /* TMXXMLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TMXXMLParser.m; sourceTree = ""; }; + 506EE0D710304F7500A389B3 /* TouchDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TouchDelegateProtocol.h; sourceTree = ""; }; + 506EE0D810304F7500A389B3 /* TouchDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TouchDispatcher.h; sourceTree = ""; }; + 506EE0D910304F7500A389B3 /* TouchDispatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TouchDispatcher.m; sourceTree = ""; }; + 506EE0DA10304F7500A389B3 /* TouchHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TouchHandler.h; sourceTree = ""; }; + 506EE0DB10304F7500A389B3 /* TouchHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TouchHandler.m; sourceTree = ""; }; + 506EE0DC10304F7500A389B3 /* Transition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Transition.h; sourceTree = ""; }; + 506EE0DD10304F7500A389B3 /* Transition.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Transition.m; sourceTree = ""; }; + 506EE0DF10304F7500A389B3 /* CDAudioManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDAudioManager.h; sourceTree = ""; }; + 506EE0E010304F7500A389B3 /* CDAudioManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDAudioManager.m; sourceTree = ""; }; + 506EE0E110304F7500A389B3 /* CDOpenALSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDOpenALSupport.h; sourceTree = ""; }; + 506EE0E210304F7500A389B3 /* CocosDenshion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CocosDenshion.h; sourceTree = ""; }; + 506EE0E310304F7500A389B3 /* CocosDenshion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CocosDenshion.m; sourceTree = ""; }; + 506EE0E410304F7500A389B3 /* SimpleAudioEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleAudioEngine.h; sourceTree = ""; }; + 506EE0E510304F7500A389B3 /* SimpleAudioEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SimpleAudioEngine.m; sourceTree = ""; }; + 506EE0E710304F7500A389B3 /* cocoslive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cocoslive.h; sourceTree = ""; }; + 506EE0E810304F7500A389B3 /* cocoslive.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = cocoslive.m; sourceTree = ""; }; + 506EE0E910304F7500A389B3 /* ScoreServerPost.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScoreServerPost.h; sourceTree = ""; }; + 506EE0EA10304F7500A389B3 /* ScoreServerPost.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScoreServerPost.m; sourceTree = ""; }; + 506EE0EB10304F7500A389B3 /* ScoreServerRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScoreServerRequest.h; sourceTree = ""; }; + 506EE0EC10304F7500A389B3 /* ScoreServerRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScoreServerRequest.m; sourceTree = ""; }; + 506EE0EE10304F7500A389B3 /* CDataScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDataScanner.h; sourceTree = ""; }; + 506EE0EF10304F7500A389B3 /* CDataScanner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDataScanner.m; sourceTree = ""; }; + 506EE0F110304F7500A389B3 /* CDataScanner_Extensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDataScanner_Extensions.h; sourceTree = ""; }; + 506EE0F210304F7500A389B3 /* CDataScanner_Extensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDataScanner_Extensions.m; sourceTree = ""; }; + 506EE0F310304F7500A389B3 /* NSCharacterSet_Extensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSCharacterSet_Extensions.h; sourceTree = ""; }; + 506EE0F410304F7500A389B3 /* NSCharacterSet_Extensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSCharacterSet_Extensions.m; sourceTree = ""; }; + 506EE0F510304F7500A389B3 /* NSDictionary_JSONExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSDictionary_JSONExtensions.h; sourceTree = ""; }; + 506EE0F610304F7500A389B3 /* NSDictionary_JSONExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSDictionary_JSONExtensions.m; sourceTree = ""; }; + 506EE0F710304F7500A389B3 /* NSScanner_Extensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSScanner_Extensions.h; sourceTree = ""; }; + 506EE0F810304F7500A389B3 /* NSScanner_Extensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSScanner_Extensions.m; sourceTree = ""; }; + 506EE0FA10304F7500A389B3 /* CJSONDeserializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CJSONDeserializer.h; sourceTree = ""; }; + 506EE0FB10304F7500A389B3 /* CJSONDeserializer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CJSONDeserializer.m; sourceTree = ""; }; + 506EE0FC10304F7500A389B3 /* CJSONScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CJSONScanner.h; sourceTree = ""; }; + 506EE0FD10304F7500A389B3 /* CJSONScanner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CJSONScanner.m; sourceTree = ""; }; + 506EE0FE10304F7500A389B3 /* CJSONSerializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CJSONSerializer.h; sourceTree = ""; }; + 506EE0FF10304F7500A389B3 /* CJSONSerializer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CJSONSerializer.m; sourceTree = ""; }; + 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CA3EACB80FEE7024004BBF8E /* fps_images.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = fps_images.png; 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; }; +/* 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 /* Tetris.app */, + 506EE05E10304ED200A389B3 /* libcocos2d libraries.a */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + isa = PBXGroup; + children = ( + 506EDAA3102F461B00A389B3 /* cocos2d Sources */, + 2D500B1D0D5A766B00DBA0E3 /* Classes */, + 29B97315FDCFA39411CA2CEA /* Other Sources */, + 29B97317FDCFA39411CA2CEA /* Resources */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = CustomTemplate; + sourceTree = ""; + }; + 29B97315FDCFA39411CA2CEA /* Other Sources */ = { + isa = PBXGroup; + children = ( + 1F3B9A820EF2151B00286867 /* Tetris_Prefix.pch */, + 29B97316FDCFA39411CA2CEA /* main.m */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + 29B97317FDCFA39411CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + 37E45F6310717B9500B2E7D9 /* gameover.jpeg */, + 37E45DBE10715E1600B2E7D9 /* blue.png */, + 37E45DBF10715E1600B2E7D9 /* cyan.png */, + 37E45DC010715E1600B2E7D9 /* green.png */, + 37E45DC110715E1600B2E7D9 /* magenta.png */, + 37E45DC210715E1600B2E7D9 /* orange.png */, + 37E45DC310715E1600B2E7D9 /* red.png */, + 37E45DC410715E1600B2E7D9 /* yellow.png */, + 37BC6F8A106EE72300EF5C76 /* background.jpg */, + 505573C11045D1EA00A31725 /* Default.png */, + 505573BF1045D1BE00A31725 /* Icon.png */, + CA3EACB80FEE7024004BBF8E /* fps_images.png */, + 8D1107310486CEB800E47090 /* Info.plist */, + ); + name = Resources; + 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 = ( + 37E45D591071507400B2E7D9 /* Tetromino.h */, + 37E45D5A1071507400B2E7D9 /* Tetromino.m */, + 37BC6F74106EE4D200EF5C76 /* GameScene.h */, + 37BC6F75106EE4D200EF5C76 /* GameScene.m */, + 37BC6FE7106F03D900EF5C76 /* GameLogicLayer.h */, + 37BC6FE8106F03D900EF5C76 /* GameLogicLayer.m */, + 1F3B9A2C0EF2145700286867 /* TetrisAppDelegate.h */, + 1F3B9A2B0EF2145700286867 /* TetrisAppDelegate.m */, + 37BC6FA8106EEC8000EF5C76 /* Block.h */, + 37BC6FA9106EEC8000EF5C76 /* Block.m */, + ); + path = Classes; + sourceTree = ""; + }; + 506EDAA3102F461B00A389B3 /* cocos2d Sources */ = { + isa = PBXGroup; + children = ( + 506EE06610304F7500A389B3 /* cocos2d */, + 506EE0DE10304F7500A389B3 /* CocosDenshion */, + 506EE0E610304F7500A389B3 /* cocoslive */, + 506EE0ED10304F7500A389B3 /* TouchJSON */, + ); + name = "cocos2d Sources"; + sourceTree = ""; + }; + 506EE06610304F7500A389B3 /* cocos2d */ = { + isa = PBXGroup; + children = ( + 506EE06710304F7500A389B3 /* Action.h */, + 506EE06810304F7500A389B3 /* Action.m */, + 506EE06910304F7500A389B3 /* ActionManager.h */, + 506EE06A10304F7500A389B3 /* ActionManager.m */, + 506EE06B10304F7500A389B3 /* AtlasNode.h */, + 506EE06C10304F7500A389B3 /* AtlasNode.m */, + 506EE06D10304F7500A389B3 /* AtlasSprite.h */, + 506EE06E10304F7500A389B3 /* AtlasSprite.m */, + 506EE06F10304F7500A389B3 /* AtlasSpriteManager.h */, + 506EE07010304F7500A389B3 /* AtlasSpriteManager.m */, + 506EE07110304F7500A389B3 /* BitmapFontAtlas.h */, + 506EE07210304F7500A389B3 /* BitmapFontAtlas.m */, + 506EE07310304F7500A389B3 /* Camera.h */, + 506EE07410304F7500A389B3 /* Camera.m */, + 506EE07510304F7500A389B3 /* CameraAction.h */, + 506EE07610304F7500A389B3 /* CameraAction.m */, + 506EE07710304F7500A389B3 /* ccExceptions.h */, + 506EE07810304F7500A389B3 /* ccMacros.h */, + 506EE07910304F7500A389B3 /* ccTypes.h */, + 506EE07A10304F7500A389B3 /* cocos2d.h */, + 506EE07B10304F7500A389B3 /* cocos2d.m */, + 506EE07C10304F7500A389B3 /* CocosNode.h */, + 506EE07D10304F7500A389B3 /* CocosNode.m */, + 506EE07E10304F7500A389B3 /* Director.h */, + 506EE07F10304F7500A389B3 /* Director.m */, + 506EE08010304F7500A389B3 /* DrawingPrimitives.h */, + 506EE08110304F7500A389B3 /* DrawingPrimitives.m */, + 506EE08210304F7500A389B3 /* EaseAction.h */, + 506EE08310304F7500A389B3 /* EaseAction.m */, + 506EE08410304F7500A389B3 /* Grabber.h */, + 506EE08510304F7500A389B3 /* Grabber.m */, + 506EE08610304F7500A389B3 /* Grid.h */, + 506EE08710304F7500A389B3 /* Grid.m */, + 506EE08810304F7500A389B3 /* Grid3DAction.h */, + 506EE08910304F7500A389B3 /* Grid3DAction.m */, + 506EE08A10304F7500A389B3 /* GridAction.h */, + 506EE08B10304F7500A389B3 /* GridAction.m */, + 506EE08C10304F7500A389B3 /* InstantAction.h */, + 506EE08D10304F7500A389B3 /* InstantAction.m */, + 506EE08E10304F7500A389B3 /* IntervalAction.h */, + 506EE08F10304F7500A389B3 /* IntervalAction.m */, + 506EE09010304F7500A389B3 /* Label.h */, + 506EE09110304F7500A389B3 /* Label.m */, + 506EE09210304F7500A389B3 /* LabelAtlas.h */, + 506EE09310304F7500A389B3 /* LabelAtlas.m */, + 506EE09410304F7500A389B3 /* Layer.h */, + 506EE09510304F7500A389B3 /* Layer.m */, + 506EE09610304F7500A389B3 /* Menu.h */, + 506EE09710304F7500A389B3 /* Menu.m */, + 506EE09810304F7500A389B3 /* MenuItem.h */, + 506EE09910304F7500A389B3 /* MenuItem.m */, + 506EE09A10304F7500A389B3 /* MotionStreak.h */, + 506EE09B10304F7500A389B3 /* MotionStreak.m */, + 506EE09C10304F7500A389B3 /* ParallaxNode.h */, + 506EE09D10304F7500A389B3 /* ParallaxNode.m */, + 506EE09E10304F7500A389B3 /* ParticleExamples.h */, + 506EE09F10304F7500A389B3 /* ParticleExamples.m */, + 506EE0A010304F7500A389B3 /* ParticleSystem.h */, + 506EE0A110304F7500A389B3 /* ParticleSystem.m */, + 506EE0A210304F7500A389B3 /* PointParticleSystem.h */, + 506EE0A310304F7500A389B3 /* PointParticleSystem.m */, + 506EE0A410304F7500A389B3 /* QuadParticleSystem.h */, + 506EE0A510304F7500A389B3 /* QuadParticleSystem.m */, + 506EE0A610304F7500A389B3 /* RenderTexture.h */, + 506EE0A710304F7500A389B3 /* RenderTexture.m */, + 506EE0A810304F7500A389B3 /* Ribbon.h */, + 506EE0A910304F7500A389B3 /* Ribbon.m */, + 506EE0AA10304F7500A389B3 /* Scene.h */, + 506EE0AB10304F7500A389B3 /* Scene.m */, + 506EE0AC10304F7500A389B3 /* Scheduler.h */, + 506EE0AD10304F7500A389B3 /* Scheduler.m */, + 506EE0AE10304F7500A389B3 /* Sprite.h */, + 506EE0AF10304F7500A389B3 /* Sprite.m */, + 506EE0B010304F7500A389B3 /* Support */, + 506EE0C910304F7500A389B3 /* TextureAtlas.h */, + 506EE0CA10304F7500A389B3 /* TextureAtlas.m */, + 506EE0CB10304F7500A389B3 /* TextureMgr.h */, + 506EE0CC10304F7500A389B3 /* TextureMgr.m */, + 506EE0CD10304F7500A389B3 /* TextureNode.h */, + 506EE0CE10304F7500A389B3 /* TextureNode.m */, + 506EE0CF10304F7500A389B3 /* TiledGridAction.h */, + 506EE0D010304F7500A389B3 /* TiledGridAction.m */, + 506EE0D110304F7500A389B3 /* TileMapAtlas.h */, + 506EE0D210304F7500A389B3 /* TileMapAtlas.m */, + 506EE0D310304F7500A389B3 /* TMXTiledMap.h */, + 506EE0D410304F7500A389B3 /* TMXTiledMap.m */, + 506EE0D510304F7500A389B3 /* TMXXMLParser.h */, + 506EE0D610304F7500A389B3 /* TMXXMLParser.m */, + 506EE0D710304F7500A389B3 /* TouchDelegateProtocol.h */, + 506EE0D810304F7500A389B3 /* TouchDispatcher.h */, + 506EE0D910304F7500A389B3 /* TouchDispatcher.m */, + 506EE0DA10304F7500A389B3 /* TouchHandler.h */, + 506EE0DB10304F7500A389B3 /* TouchHandler.m */, + 506EE0DC10304F7500A389B3 /* Transition.h */, + 506EE0DD10304F7500A389B3 /* Transition.m */, + ); + path = cocos2d; + sourceTree = ""; + }; + 506EE0B010304F7500A389B3 /* Support */ = { + isa = PBXGroup; + children = ( + 506EE0B110304F7500A389B3 /* base64.c */, + 506EE0B210304F7500A389B3 /* base64.h */, + 506EE0B310304F7500A389B3 /* ccArray.h */, + 506EE0B410304F7500A389B3 /* ccHashSet.h */, + 506EE0B510304F7500A389B3 /* ccHashSet.m */, + 506EE0B610304F7500A389B3 /* CGPointExtension.h */, + 506EE0B710304F7500A389B3 /* CGPointExtension.m */, + 506EE0B810304F7500A389B3 /* EAGLView.h */, + 506EE0B910304F7500A389B3 /* EAGLView.m */, + 506EE0BA10304F7500A389B3 /* FileUtils.h */, + 506EE0BB10304F7500A389B3 /* FileUtils.m */, + 506EE0BC10304F7500A389B3 /* glu.c */, + 506EE0BD10304F7500A389B3 /* glu.h */, + 506EE0BE10304F7500A389B3 /* OpenGL_Internal.h */, + 506EE0BF10304F7500A389B3 /* PVRTexture.h */, + 506EE0C010304F7500A389B3 /* PVRTexture.m */, + 506EE0C110304F7500A389B3 /* Texture2D.h */, + 506EE0C210304F7500A389B3 /* Texture2D.m */, + 506EE0C310304F7500A389B3 /* TGAlib.h */, + 506EE0C410304F7500A389B3 /* TGAlib.m */, + 506EE0C510304F7500A389B3 /* TransformUtils.h */, + 506EE0C610304F7500A389B3 /* TransformUtils.m */, + 506EE0C710304F7500A389B3 /* ZipUtils.h */, + 506EE0C810304F7500A389B3 /* ZipUtils.m */, + ); + path = Support; + sourceTree = ""; + }; + 506EE0DE10304F7500A389B3 /* CocosDenshion */ = { + isa = PBXGroup; + children = ( + 506EE0DF10304F7500A389B3 /* CDAudioManager.h */, + 506EE0E010304F7500A389B3 /* CDAudioManager.m */, + 506EE0E110304F7500A389B3 /* CDOpenALSupport.h */, + 506EE0E210304F7500A389B3 /* CocosDenshion.h */, + 506EE0E310304F7500A389B3 /* CocosDenshion.m */, + 506EE0E410304F7500A389B3 /* SimpleAudioEngine.h */, + 506EE0E510304F7500A389B3 /* SimpleAudioEngine.m */, + ); + path = CocosDenshion; + sourceTree = ""; + }; + 506EE0E610304F7500A389B3 /* cocoslive */ = { + isa = PBXGroup; + children = ( + 506EE0E710304F7500A389B3 /* cocoslive.h */, + 506EE0E810304F7500A389B3 /* cocoslive.m */, + 506EE0E910304F7500A389B3 /* ScoreServerPost.h */, + 506EE0EA10304F7500A389B3 /* ScoreServerPost.m */, + 506EE0EB10304F7500A389B3 /* ScoreServerRequest.h */, + 506EE0EC10304F7500A389B3 /* ScoreServerRequest.m */, + ); + path = cocoslive; + sourceTree = ""; + }; + 506EE0ED10304F7500A389B3 /* TouchJSON */ = { + isa = PBXGroup; + children = ( + 506EE0EE10304F7500A389B3 /* CDataScanner.h */, + 506EE0EF10304F7500A389B3 /* CDataScanner.m */, + 506EE0F010304F7500A389B3 /* Extensions */, + 506EE0F910304F7500A389B3 /* JSON */, + ); + path = TouchJSON; + sourceTree = ""; + }; + 506EE0F010304F7500A389B3 /* Extensions */ = { + isa = PBXGroup; + children = ( + 506EE0F110304F7500A389B3 /* CDataScanner_Extensions.h */, + 506EE0F210304F7500A389B3 /* CDataScanner_Extensions.m */, + 506EE0F310304F7500A389B3 /* NSCharacterSet_Extensions.h */, + 506EE0F410304F7500A389B3 /* NSCharacterSet_Extensions.m */, + 506EE0F510304F7500A389B3 /* NSDictionary_JSONExtensions.h */, + 506EE0F610304F7500A389B3 /* NSDictionary_JSONExtensions.m */, + 506EE0F710304F7500A389B3 /* NSScanner_Extensions.h */, + 506EE0F810304F7500A389B3 /* NSScanner_Extensions.m */, + ); + path = Extensions; + sourceTree = ""; + }; + 506EE0F910304F7500A389B3 /* JSON */ = { + isa = PBXGroup; + children = ( + 506EE0FA10304F7500A389B3 /* CJSONDeserializer.h */, + 506EE0FB10304F7500A389B3 /* CJSONDeserializer.m */, + 506EE0FC10304F7500A389B3 /* CJSONScanner.h */, + 506EE0FD10304F7500A389B3 /* CJSONScanner.m */, + 506EE0FE10304F7500A389B3 /* CJSONSerializer.h */, + 506EE0FF10304F7500A389B3 /* CJSONSerializer.m */, + ); + path = JSON; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 506EE05A10304ED200A389B3 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 506EE10010304F7500A389B3 /* Action.h in Headers */, + 506EE10210304F7500A389B3 /* ActionManager.h in Headers */, + 506EE10410304F7500A389B3 /* AtlasNode.h in Headers */, + 506EE10610304F7500A389B3 /* AtlasSprite.h in Headers */, + 506EE10810304F7500A389B3 /* AtlasSpriteManager.h in Headers */, + 506EE10A10304F7500A389B3 /* BitmapFontAtlas.h in Headers */, + 506EE10C10304F7500A389B3 /* Camera.h in Headers */, + 506EE10E10304F7500A389B3 /* CameraAction.h in Headers */, + 506EE11010304F7500A389B3 /* ccExceptions.h in Headers */, + 506EE11110304F7500A389B3 /* ccMacros.h in Headers */, + 506EE11210304F7500A389B3 /* ccTypes.h in Headers */, + 506EE11310304F7500A389B3 /* cocos2d.h in Headers */, + 506EE11510304F7500A389B3 /* CocosNode.h in Headers */, + 506EE11710304F7500A389B3 /* Director.h in Headers */, + 506EE11910304F7500A389B3 /* DrawingPrimitives.h in Headers */, + 506EE11B10304F7500A389B3 /* EaseAction.h in Headers */, + 506EE11D10304F7500A389B3 /* Grabber.h in Headers */, + 506EE11F10304F7500A389B3 /* Grid.h in Headers */, + 506EE12110304F7500A389B3 /* Grid3DAction.h in Headers */, + 506EE12310304F7500A389B3 /* GridAction.h in Headers */, + 506EE12510304F7500A389B3 /* InstantAction.h in Headers */, + 506EE12710304F7500A389B3 /* IntervalAction.h in Headers */, + 506EE12910304F7500A389B3 /* Label.h in Headers */, + 506EE12B10304F7500A389B3 /* LabelAtlas.h in Headers */, + 506EE12D10304F7500A389B3 /* Layer.h in Headers */, + 506EE12F10304F7500A389B3 /* Menu.h in Headers */, + 506EE13110304F7500A389B3 /* MenuItem.h in Headers */, + 506EE13310304F7500A389B3 /* MotionStreak.h in Headers */, + 506EE13510304F7500A389B3 /* ParallaxNode.h in Headers */, + 506EE13710304F7500A389B3 /* ParticleExamples.h in Headers */, + 506EE13910304F7500A389B3 /* ParticleSystem.h in Headers */, + 506EE13B10304F7500A389B3 /* PointParticleSystem.h in Headers */, + 506EE13D10304F7500A389B3 /* QuadParticleSystem.h in Headers */, + 506EE13F10304F7500A389B3 /* RenderTexture.h in Headers */, + 506EE14110304F7500A389B3 /* Ribbon.h in Headers */, + 506EE14310304F7500A389B3 /* Scene.h in Headers */, + 506EE14510304F7500A389B3 /* Scheduler.h in Headers */, + 506EE14710304F7500A389B3 /* Sprite.h in Headers */, + 506EE14A10304F7500A389B3 /* base64.h in Headers */, + 506EE14B10304F7500A389B3 /* ccArray.h in Headers */, + 506EE14C10304F7500A389B3 /* ccHashSet.h in Headers */, + 506EE14E10304F7500A389B3 /* CGPointExtension.h in Headers */, + 506EE15010304F7500A389B3 /* EAGLView.h in Headers */, + 506EE15210304F7500A389B3 /* FileUtils.h in Headers */, + 506EE15510304F7500A389B3 /* glu.h in Headers */, + 506EE15610304F7500A389B3 /* OpenGL_Internal.h in Headers */, + 506EE15710304F7500A389B3 /* PVRTexture.h in Headers */, + 506EE15910304F7500A389B3 /* Texture2D.h in Headers */, + 506EE15B10304F7500A389B3 /* TGAlib.h in Headers */, + 506EE15D10304F7500A389B3 /* TransformUtils.h in Headers */, + 506EE15F10304F7500A389B3 /* ZipUtils.h in Headers */, + 506EE16110304F7500A389B3 /* TextureAtlas.h in Headers */, + 506EE16310304F7500A389B3 /* TextureMgr.h in Headers */, + 506EE16510304F7500A389B3 /* TextureNode.h in Headers */, + 506EE16710304F7500A389B3 /* TiledGridAction.h in Headers */, + 506EE16910304F7500A389B3 /* TileMapAtlas.h in Headers */, + 506EE16B10304F7500A389B3 /* TMXTiledMap.h in Headers */, + 506EE16D10304F7500A389B3 /* TMXXMLParser.h in Headers */, + 506EE16F10304F7500A389B3 /* TouchDelegateProtocol.h in Headers */, + 506EE17010304F7500A389B3 /* TouchDispatcher.h in Headers */, + 506EE17210304F7500A389B3 /* TouchHandler.h in Headers */, + 506EE17410304F7500A389B3 /* Transition.h in Headers */, + 506EE17610304F7500A389B3 /* CDAudioManager.h in Headers */, + 506EE17810304F7500A389B3 /* CDOpenALSupport.h in Headers */, + 506EE17910304F7500A389B3 /* CocosDenshion.h in Headers */, + 506EE17B10304F7500A389B3 /* SimpleAudioEngine.h in Headers */, + 506EE17D10304F7500A389B3 /* cocoslive.h in Headers */, + 506EE17F10304F7500A389B3 /* ScoreServerPost.h in Headers */, + 506EE18110304F7500A389B3 /* ScoreServerRequest.h in Headers */, + 506EE18310304F7500A389B3 /* CDataScanner.h in Headers */, + 506EE18510304F7500A389B3 /* CDataScanner_Extensions.h in Headers */, + 506EE18710304F7500A389B3 /* NSCharacterSet_Extensions.h in Headers */, + 506EE18910304F7500A389B3 /* NSDictionary_JSONExtensions.h in Headers */, + 506EE18B10304F7500A389B3 /* NSScanner_Extensions.h in Headers */, + 506EE18D10304F7500A389B3 /* CJSONDeserializer.h in Headers */, + 506EE18F10304F7500A389B3 /* CJSONScanner.h in Headers */, + 506EE19110304F7500A389B3 /* CJSONSerializer.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 1D6058900D05DD3D006BFB54 /* Tetris */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "Tetris" */; + buildPhases = ( + 1D60588D0D05DD3D006BFB54 /* Resources */, + 1D60588E0D05DD3D006BFB54 /* Sources */, + 1D60588F0D05DD3D006BFB54 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 506EE1A81030507B00A389B3 /* PBXTargetDependency */, + ); + name = Tetris; + productName = Tetris; + productReference = 1D6058910D05DD3D006BFB54 /* Tetris.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 "Tetris" */; + compatibilityVersion = "Xcode 3.1"; + hasScannedForEncodings = 1; + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D6058900D05DD3D006BFB54 /* Tetris */, + 506EE05D10304ED200A389B3 /* cocos2d libraries */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1D60588D0D05DD3D006BFB54 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CA3EACB90FEE7024004BBF8E /* fps_images.png in Resources */, + 505573C01045D1BE00A31725 /* Icon.png in Resources */, + 505573C21045D1EA00A31725 /* Default.png in Resources */, + 37BC6F8B106EE72300EF5C76 /* background.jpg in Resources */, + 37E45DC510715E1600B2E7D9 /* blue.png in Resources */, + 37E45DC610715E1600B2E7D9 /* cyan.png in Resources */, + 37E45DC710715E1600B2E7D9 /* green.png in Resources */, + 37E45DC810715E1600B2E7D9 /* magenta.png in Resources */, + 37E45DC910715E1600B2E7D9 /* orange.png in Resources */, + 37E45DCA10715E1600B2E7D9 /* red.png in Resources */, + 37E45DCB10715E1600B2E7D9 /* yellow.png in Resources */, + 37E45F6410717B9500B2E7D9 /* gameover.jpeg in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D60588E0D05DD3D006BFB54 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D60589B0D05DD56006BFB54 /* main.m in Sources */, + 1F3B9A2D0EF2145700286867 /* TetrisAppDelegate.m in Sources */, + 37BC6F76106EE4D200EF5C76 /* GameScene.m in Sources */, + 37BC6FAA106EEC8000EF5C76 /* Block.m in Sources */, + 37BC6FE9106F03D900EF5C76 /* GameLogicLayer.m in Sources */, + 37E45D5B1071507400B2E7D9 /* Tetromino.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 506EE05B10304ED200A389B3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 506EE10110304F7500A389B3 /* Action.m in Sources */, + 506EE10310304F7500A389B3 /* ActionManager.m in Sources */, + 506EE10510304F7500A389B3 /* AtlasNode.m in Sources */, + 506EE10710304F7500A389B3 /* AtlasSprite.m in Sources */, + 506EE10910304F7500A389B3 /* AtlasSpriteManager.m in Sources */, + 506EE10B10304F7500A389B3 /* BitmapFontAtlas.m in Sources */, + 506EE10D10304F7500A389B3 /* Camera.m in Sources */, + 506EE10F10304F7500A389B3 /* CameraAction.m in Sources */, + 506EE11410304F7500A389B3 /* cocos2d.m in Sources */, + 506EE11610304F7500A389B3 /* CocosNode.m in Sources */, + 506EE11810304F7500A389B3 /* Director.m in Sources */, + 506EE11A10304F7500A389B3 /* DrawingPrimitives.m in Sources */, + 506EE11C10304F7500A389B3 /* EaseAction.m in Sources */, + 506EE11E10304F7500A389B3 /* Grabber.m in Sources */, + 506EE12010304F7500A389B3 /* Grid.m in Sources */, + 506EE12210304F7500A389B3 /* Grid3DAction.m in Sources */, + 506EE12410304F7500A389B3 /* GridAction.m in Sources */, + 506EE12610304F7500A389B3 /* InstantAction.m in Sources */, + 506EE12810304F7500A389B3 /* IntervalAction.m in Sources */, + 506EE12A10304F7500A389B3 /* Label.m in Sources */, + 506EE12C10304F7500A389B3 /* LabelAtlas.m in Sources */, + 506EE12E10304F7500A389B3 /* Layer.m in Sources */, + 506EE13010304F7500A389B3 /* Menu.m in Sources */, + 506EE13210304F7500A389B3 /* MenuItem.m in Sources */, + 506EE13410304F7500A389B3 /* MotionStreak.m in Sources */, + 506EE13610304F7500A389B3 /* ParallaxNode.m in Sources */, + 506EE13810304F7500A389B3 /* ParticleExamples.m in Sources */, + 506EE13A10304F7500A389B3 /* ParticleSystem.m in Sources */, + 506EE13C10304F7500A389B3 /* PointParticleSystem.m in Sources */, + 506EE13E10304F7500A389B3 /* QuadParticleSystem.m in Sources */, + 506EE14010304F7500A389B3 /* RenderTexture.m in Sources */, + 506EE14210304F7500A389B3 /* Ribbon.m in Sources */, + 506EE14410304F7500A389B3 /* Scene.m in Sources */, + 506EE14610304F7500A389B3 /* Scheduler.m in Sources */, + 506EE14810304F7500A389B3 /* Sprite.m in Sources */, + 506EE14910304F7500A389B3 /* base64.c in Sources */, + 506EE14D10304F7500A389B3 /* ccHashSet.m in Sources */, + 506EE14F10304F7500A389B3 /* CGPointExtension.m in Sources */, + 506EE15110304F7500A389B3 /* EAGLView.m in Sources */, + 506EE15310304F7500A389B3 /* FileUtils.m in Sources */, + 506EE15410304F7500A389B3 /* glu.c in Sources */, + 506EE15810304F7500A389B3 /* PVRTexture.m in Sources */, + 506EE15A10304F7500A389B3 /* Texture2D.m in Sources */, + 506EE15C10304F7500A389B3 /* TGAlib.m in Sources */, + 506EE15E10304F7500A389B3 /* TransformUtils.m in Sources */, + 506EE16010304F7500A389B3 /* ZipUtils.m in Sources */, + 506EE16210304F7500A389B3 /* TextureAtlas.m in Sources */, + 506EE16410304F7500A389B3 /* TextureMgr.m in Sources */, + 506EE16610304F7500A389B3 /* TextureNode.m in Sources */, + 506EE16810304F7500A389B3 /* TiledGridAction.m in Sources */, + 506EE16A10304F7500A389B3 /* TileMapAtlas.m in Sources */, + 506EE16C10304F7500A389B3 /* TMXTiledMap.m in Sources */, + 506EE16E10304F7500A389B3 /* TMXXMLParser.m in Sources */, + 506EE17110304F7500A389B3 /* TouchDispatcher.m in Sources */, + 506EE17310304F7500A389B3 /* TouchHandler.m in Sources */, + 506EE17510304F7500A389B3 /* Transition.m in Sources */, + 506EE17710304F7500A389B3 /* CDAudioManager.m in Sources */, + 506EE17A10304F7500A389B3 /* CocosDenshion.m in Sources */, + 506EE17C10304F7500A389B3 /* SimpleAudioEngine.m in Sources */, + 506EE17E10304F7500A389B3 /* cocoslive.m in Sources */, + 506EE18010304F7500A389B3 /* ScoreServerPost.m in Sources */, + 506EE18210304F7500A389B3 /* ScoreServerRequest.m in Sources */, + 506EE18410304F7500A389B3 /* CDataScanner.m in Sources */, + 506EE18610304F7500A389B3 /* CDataScanner_Extensions.m in Sources */, + 506EE18810304F7500A389B3 /* NSCharacterSet_Extensions.m in Sources */, + 506EE18A10304F7500A389B3 /* NSDictionary_JSONExtensions.m in Sources */, + 506EE18C10304F7500A389B3 /* NSScanner_Extensions.m in Sources */, + 506EE18E10304F7500A389B3 /* CJSONDeserializer.m in Sources */, + 506EE19010304F7500A389B3 /* CJSONScanner.m in Sources */, + 506EE19210304F7500A389B3 /* 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 = Tetris_Prefix.pch; + GCC_PREPROCESSOR_DEFINITIONS = "COCOS2D_DEBUG=1"; + GCC_THUMB_SUPPORT = NO; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + INFOPLIST_FILE = Info.plist; + OTHER_LDFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = Tetris; + 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 = Tetris_Prefix.pch; + GCC_THUMB_SUPPORT = NO; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + INFOPLIST_FILE = Info.plist; + OTHER_LDFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = Tetris; + 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_PREPROCESSOR_DEFINITIONS = "COCOS2D_DEBUG=1"; + GCC_THUMB_SUPPORT = NO; + 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_THUMB_SUPPORT = 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_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + PREBINDING = NO; + SDKROOT = iphoneos2.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_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = iphoneos2.2; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "Tetris" */ = { + 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 "Tetris" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/Tetris_Prefix.pch b/Tetris_Prefix.pch new file mode 100755 index 0000000..da2d8ec --- /dev/null +++ b/Tetris_Prefix.pch @@ -0,0 +1,8 @@ +// +// Prefix header for all source files of the 'Tetris' target in the 'Tetris' project +// + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/TouchJSON/CDataScanner.h b/TouchJSON/CDataScanner.h new file mode 100644 index 0000000..a768892 --- /dev/null +++ b/TouchJSON/CDataScanner.h @@ -0,0 +1,68 @@ +// +// CDataScanner.h +// TouchJSON +// +// Created by Jonathan Wight on 04/16/08. +// Copyright (c) 2008 Jonathan Wight +// +// 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; + + NSCharacterSet *doubleCharacters; +} + +@property (readwrite, nonatomic, retain) NSData *data; +@property (readwrite, nonatomic, assign) NSUInteger scanLocation; +@property (readonly, nonatomic, assign) BOOL isAtEnd; + ++ (id)scannerWithData:(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; + +- (void)skipWhitespace; + +- (NSString *)remainingString; + +@end diff --git a/TouchJSON/CDataScanner.m b/TouchJSON/CDataScanner.m new file mode 100644 index 0000000..44df309 --- /dev/null +++ b/TouchJSON/CDataScanner.m @@ -0,0 +1,270 @@ +// +// CDataScanner.m +// TouchJSON +// +// Created by Jonathan Wight on 04/16/08. +// Copyright (c) 2008 Jonathan Wight +// +// 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 () +@property (readwrite, nonatomic, retain) NSCharacterSet *doubleCharacters; +@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); +} + +@implementation CDataScanner + +@dynamic data; +@dynamic scanLocation; +@dynamic isAtEnd; +@synthesize doubleCharacters; + ++ (id)scannerWithData:(NSData *)inData +{ +CDataScanner *theScanner = [[[self alloc] init] autorelease]; +theScanner.data = inData; +return(theScanner); +} + +- (id)init +{ +if ((self = [super init]) != nil) + { + self.doubleCharacters = [NSCharacterSet characterSetWithCharactersInString:@"0123456789eE-."]; + } +return(self); +} + +- (void)dealloc +{ +self.data = NULL; +self.doubleCharacters = NULL; +// +[super dealloc]; +} + +- (NSUInteger)scanLocation +{ +return(current - start); +} + +- (NSData *)data +{ +return(data); +} + +- (void)setData:(NSData *)inData +{ +if (data != inData) + { + if (data) + { + [data release]; + data = NULL; + } + + if (inData) + { + data = [inData retain]; + // + start = (u_int8_t *)data.bytes; + end = start + data.length; + current = start; + length = data.length; + } + } +} + +- (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 +{ +// Replace all of this with a strtod call +NSString *theString = NULL; +if ([self scanCharactersFromSet:doubleCharacters intoString:&theString]) + { + if (outValue) + *outValue = [NSNumber numberWithDouble:[theString doubleValue]]; // TODO dont use doubleValue + return(YES); + } +return(NO); +} + +- (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); +} + +@end diff --git a/TouchJSON/Extensions/CDataScanner_Extensions.h b/TouchJSON/Extensions/CDataScanner_Extensions.h new file mode 100644 index 0000000..8c3c8dd --- /dev/null +++ b/TouchJSON/Extensions/CDataScanner_Extensions.h @@ -0,0 +1,37 @@ +// +// CDataScanner_Extensions.h +// TouchJSON +// +// Created by Jonathan Wight on 12/08/2005. +// Copyright (c) 2005 Jonathan Wight +// +// 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; + +@end diff --git a/TouchJSON/Extensions/CDataScanner_Extensions.m b/TouchJSON/Extensions/CDataScanner_Extensions.m new file mode 100644 index 0000000..c1a16d9 --- /dev/null +++ b/TouchJSON/Extensions/CDataScanner_Extensions.m @@ -0,0 +1,80 @@ +// +// NSScanner_Extensions.m +// TouchJSON +// +// Created by Jonathan Wight on 12/08/2005. +// Copyright (c) 2005 Jonathan Wight +// +// 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" + +#import "NSCharacterSet_Extensions.h" + +@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) + { + NSString *theComment = NULL; + [self scanUpToCharactersFromSet:[NSCharacterSet linebreaksCharacterSet] intoString:&theComment]; + [self scanCharactersFromSet:[NSCharacterSet linebreaksCharacterSet] intoString:NULL]; + + if (outComment != NULL) + *outComment = theComment; + + return(YES); + } +else + { + return(NO); + } +} + +@end diff --git a/TouchJSON/Extensions/NSCharacterSet_Extensions.h b/TouchJSON/Extensions/NSCharacterSet_Extensions.h new file mode 100644 index 0000000..c934a7d --- /dev/null +++ b/TouchJSON/Extensions/NSCharacterSet_Extensions.h @@ -0,0 +1,36 @@ +// +// NSCharacterSet_Extensions.h +// TouchJSON +// +// Created by Jonathan Wight on 12/08/2005. +// Copyright (c) 2005 Jonathan Wight +// +// 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 NSCharacterSet (NSCharacterSet_Extensions) + ++ (NSCharacterSet *)linebreaksCharacterSet; + +@end diff --git a/TouchJSON/Extensions/NSCharacterSet_Extensions.m b/TouchJSON/Extensions/NSCharacterSet_Extensions.m new file mode 100644 index 0000000..f0306a5 --- /dev/null +++ b/TouchJSON/Extensions/NSCharacterSet_Extensions.m @@ -0,0 +1,48 @@ +// +// NSCharacterSet_Extensions.m +// TouchJSON +// +// Created by Jonathan Wight on 12/08/2005. +// Copyright (c) 2005 Jonathan Wight +// +// 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 "NSCharacterSet_Extensions.h" + +@implementation NSCharacterSet (NSCharacterSet_Extensions) + +#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 + ++ (NSCharacterSet *)linebreaksCharacterSet +{ +unichar theCharacters[] = { LF, FF, CR, NEL, LS, PS, }; + +return([NSCharacterSet characterSetWithCharactersInString:[NSString stringWithCharacters:theCharacters length:sizeof(theCharacters) / sizeof(*theCharacters)]]); +} + +@end diff --git a/TouchJSON/Extensions/NSDictionary_JSONExtensions.h b/TouchJSON/Extensions/NSDictionary_JSONExtensions.h new file mode 100644 index 0000000..4d88124 --- /dev/null +++ b/TouchJSON/Extensions/NSDictionary_JSONExtensions.h @@ -0,0 +1,36 @@ +// +// NSDictionary_JSONExtensions.h +// TouchJSON +// +// Created by Jonathan Wight on 04/17/08. +// Copyright (c) 2008 Jonathan Wight +// +// 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; + +@end diff --git a/TouchJSON/Extensions/NSDictionary_JSONExtensions.m b/TouchJSON/Extensions/NSDictionary_JSONExtensions.m new file mode 100644 index 0000000..005efc6 --- /dev/null +++ b/TouchJSON/Extensions/NSDictionary_JSONExtensions.m @@ -0,0 +1,41 @@ +// +// NSDictionary_JSONExtensions.m +// TouchJSON +// +// Created by Jonathan Wight on 04/17/08. +// Copyright (c) 2008 Jonathan Wight +// +// 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]); +} + +@end diff --git a/TouchJSON/Extensions/NSScanner_Extensions.h b/TouchJSON/Extensions/NSScanner_Extensions.h new file mode 100644 index 0000000..fc8c774 --- /dev/null +++ b/TouchJSON/Extensions/NSScanner_Extensions.h @@ -0,0 +1,44 @@ +// +// NSScanner_Extensions.h +// CocoaJSON +// +// Created by Jonathan Wight on 12/08/2005. +// Copyright (c) 2005 Jonathan Wight +// +// 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 NSScanner (NSScanner_Extensions) + +- (NSString *)remainingString; + +- (unichar)currentCharacter; +- (unichar)scanCharacter; +- (BOOL)scanCharacter:(unichar)inCharacter; +- (void)backtrack:(unsigned)inCount; + +- (BOOL)scanCStyleComment:(NSString **)outComment; +- (BOOL)scanCPlusPlusStyleComment:(NSString **)outComment; + +@end diff --git a/TouchJSON/Extensions/NSScanner_Extensions.m b/TouchJSON/Extensions/NSScanner_Extensions.m new file mode 100644 index 0000000..981366f --- /dev/null +++ b/TouchJSON/Extensions/NSScanner_Extensions.m @@ -0,0 +1,118 @@ +// +// NSScanner_Extensions.m +// CocoaJSON +// +// Created by Jonathan Wight on 12/08/2005. +// Copyright (c) 2005 Jonathan Wight +// +// 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_Extensions.h" + +#import "NSCharacterSet_Extensions.h" + +@implementation NSScanner (NSScanner_Extensions) + +- (NSString *)remainingString +{ +return([[self string] substringFromIndex:[self scanLocation]]); +} + +- (unichar)currentCharacter +{ +return([[self string] characterAtIndex:[self scanLocation]]); +} + +- (unichar)scanCharacter +{ +unsigned theScanLocation = [self scanLocation]; +unichar theCharacter = [[self string] characterAtIndex:theScanLocation]; +[self setScanLocation:theScanLocation + 1]; +return(theCharacter); +} + +- (BOOL)scanCharacter:(unichar)inCharacter +{ +unsigned theScanLocation = [self scanLocation]; +if ([[self string] characterAtIndex:theScanLocation] == inCharacter) + { + [self setScanLocation:theScanLocation + 1]; + return(YES); + } +else + return(NO); +} + +- (void)backtrack:(unsigned)inCount +{ +unsigned theScanLocation = [self scanLocation]; +if (inCount > theScanLocation) + [NSException raise:NSGenericException format:@"Backtracked too far."]; +[self setScanLocation:theScanLocation - inCount]; +} + +- (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) + { + NSString *theComment = NULL; + [self scanUpToCharactersFromSet:[NSCharacterSet linebreaksCharacterSet] intoString:&theComment]; + [self scanCharactersFromSet:[NSCharacterSet linebreaksCharacterSet] intoString:NULL]; + + if (outComment != NULL) + *outComment = theComment; + + return(YES); + } +else + { + return(NO); + } +} + +@end diff --git a/TouchJSON/JSON/CJSONDeserializer.h b/TouchJSON/JSON/CJSONDeserializer.h new file mode 100755 index 0000000..7a805f9 --- /dev/null +++ b/TouchJSON/JSON/CJSONDeserializer.h @@ -0,0 +1,59 @@ +// +// CJSONDeserializer.h +// TouchJSON +// +// Created by Jonathan Wight on 12/15/2005. +// Copyright (c) 2005 Jonathan Wight +// +// 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 + +extern NSString *const kJSONDeserializerErrorDomain /* = @"CJSONDeserializerErrorDomain" */; + +@protocol CDeserializerProtocol + +- (id)deserializeAsDictionary:(NSData *)inData error:(NSError **)outError; + +@end + +#pragma mark - + +@interface CJSONDeserializer : NSObject { + +} + ++ (id)deserializer; + +- (id)deserializeAsDictionary:(NSData *)inData error:(NSError **)outError; + +@end + +#pragma mark - + +@interface CJSONDeserializer (CJSONDeserializer_Deprecated) + +/// You should switch to using deserializeAsDictionary:error: instead. +- (id)deserialize:(NSData *)inData error:(NSError **)outError; + +@end diff --git a/TouchJSON/JSON/CJSONDeserializer.m b/TouchJSON/JSON/CJSONDeserializer.m new file mode 100755 index 0000000..1dbb317 --- /dev/null +++ b/TouchJSON/JSON/CJSONDeserializer.m @@ -0,0 +1,84 @@ +// +// CJSONDeserializer.m +// TouchJSON +// +// Created by Jonathan Wight on 12/15/2005. +// Copyright (c) 2005 Jonathan Wight +// +// 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"; + +@implementation CJSONDeserializer + ++ (id)deserializer +{ +return([[[self alloc] init] autorelease]); +} + +- (id)deserializeAsDictionary:(NSData *)inData error:(NSError **)outError; +{ +if (inData == NULL || [inData length] == 0) + { + if (outError) + *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:-1 userInfo:NULL]; + + return(NULL); + } +CJSONScanner *theScanner = [CJSONScanner scannerWithData:inData]; +NSDictionary *theDictionary = NULL; +if ([theScanner scanJSONDictionary:&theDictionary error:outError] == YES) + return(theDictionary); +else + return(NULL); +} + +@end + +#pragma mark - + +@implementation CJSONDeserializer (CJSONDeserializer_Deprecated) + +- (id)deserialize:(NSData *)inData error:(NSError **)outError +{ +if (inData == NULL || [inData length] == 0) + { + if (outError) + *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:-1 userInfo:NULL]; + + return(NULL); + } +CJSONScanner *theScanner = [CJSONScanner scannerWithData:inData]; +id theObject = NULL; +if ([theScanner scanJSONObject:&theObject error:outError] == YES) + return(theObject); +else + return(NULL); +} + +@end diff --git a/TouchJSON/JSON/CJSONScanner.h b/TouchJSON/JSON/CJSONScanner.h new file mode 100644 index 0000000..70d6074 --- /dev/null +++ b/TouchJSON/JSON/CJSONScanner.h @@ -0,0 +1,43 @@ +// +// CJSONScanner.h +// TouchJSON +// +// Created by Jonathan Wight on 12/07/2005. +// Copyright (c) 2005 Jonathan Wight +// +// 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 CJSONScanner : CDataScanner { +} + +- (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 /* = @"CJSONScannerErrorDomain" */; diff --git a/TouchJSON/JSON/CJSONScanner.m b/TouchJSON/JSON/CJSONScanner.m new file mode 100644 index 0000000..c9b1d58 --- /dev/null +++ b/TouchJSON/JSON/CJSONScanner.m @@ -0,0 +1,536 @@ +// +// CJSONScanner.m +// TouchJSON +// +// Created by Jonathan Wight on 12/07/2005. +// Copyright (c) 2005 Jonathan Wight +// +// 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 "NSCharacterSet_Extensions.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 = @"CJSONScannerErrorDomain"; + +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 + +- (id)init +{ +if ((self = [super init]) != nil) + { + } +return(self); +} + +- (void)dealloc +{ +// +[super dealloc]; +} + +#pragma mark - + +- (void)setData:(NSData *)inData +{ +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; + } + + if (theEncoding != NSUTF8StringEncoding) + { + NSString *theString = [[NSString alloc] initWithData:theData encoding:theEncoding]; + theData = [theString dataUsingEncoding:NSUTF8StringEncoding]; + [theString release]; + } + } +[super setData:theData]; +} + +#pragma mark - + +- (BOOL)scanJSONObject:(id *)outObject error:(NSError **)outError +{ +[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 = [NSNull null]; + } + break; + case '\"': + case '\'': + [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 '-': + [self scanJSONNumberConstant:&theObject error:outError]; + break; + case '{': + [self scanJSONDictionary:&theObject error:outError]; + break; + case '[': + [self scanJSONArray:&theObject error:outError]; + break; + default: + + break; + } + +if (outObject != NULL) + *outObject = theObject; +return(YES); +} + +- (BOOL)scanJSONDictionary:(NSDictionary **)outDictionary error:(NSError **)outError +{ +NSUInteger theScanLocation = [self scanLocation]; + +if ([self scanCharacter:'{'] == NO) + { + if (outError) + { + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Could not scan dictionary. Dictionary that does not start with '{' character.", NSLocalizedDescriptionKey, + NULL]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:-1 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) + { + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Could not scan dictionary. Failed to scan a key.", NSLocalizedDescriptionKey, + NULL]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:-2 userInfo:theUserInfo]; + } + [theDictionary release]; + return(NO); + } + + [self skipWhitespace]; + + if ([self scanCharacter:':'] == NO) + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Could not scan dictionary. Key was not terminated with a ':' character.", NSLocalizedDescriptionKey, + NULL]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:-3 userInfo:theUserInfo]; + } + [theDictionary release]; + return(NO); + } + + id theValue = NULL; + if ([self scanJSONObject:&theValue error:outError] == NO) + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Could not scan dictionary. Failed to scan a value.", NSLocalizedDescriptionKey, + NULL]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:-4 userInfo:theUserInfo]; + } + [theDictionary release]; + return(NO); + } + + [theDictionary setValue:theValue forKey:theKey]; + + [self skipWhitespace]; + if ([self scanCharacter:','] == NO) + { + if ([self currentCharacter] != '}') + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Could not scan dictionary. Key value pairs not delimited with a ',' character.", NSLocalizedDescriptionKey, + NULL]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:-5 userInfo:theUserInfo]; + } + [theDictionary release]; + return(NO); + } + break; + } + else + { + [self skipWhitespace]; + if ([self currentCharacter] == '}') + break; + } + } + +if ([self scanCharacter:'}'] == NO) + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Could not scan dictionary. Dictionary not terminated by a '}' character.", NSLocalizedDescriptionKey, + NULL]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:-6 userInfo:theUserInfo]; + } + [theDictionary release]; + return(NO); + } + +if (outDictionary != NULL) + *outDictionary = [[theDictionary copy] autorelease]; + +[theDictionary release]; + +return(YES); +} + +- (BOOL)scanJSONArray:(NSArray **)outArray error:(NSError **)outError +{ +NSUInteger theScanLocation = [self scanLocation]; + +if ([self scanCharacter:'['] == NO) + { + if (outError) + { + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Could not scan array. Array not started by a '{' character.", NSLocalizedDescriptionKey, + NULL]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:-7 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) + { + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Could not scan array. Could not scan a value.", NSLocalizedDescriptionKey, + NULL]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:-8 userInfo:theUserInfo]; + } + [theArray release]; + return(NO); + } + + [theArray addObject:theValue]; + + [self skipWhitespace]; + if ([self scanCharacter:','] == NO) + { + [self skipWhitespace]; + if ([self currentCharacter] != ']') + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Could not scan array. Array not terminated by a ']' character.", NSLocalizedDescriptionKey, + NULL]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:-9 userInfo:theUserInfo]; + } + [theArray release]; + return(NO); + } + + break; + } + [self skipWhitespace]; + } + +[self skipWhitespace]; + +if ([self scanCharacter:']'] == NO) + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Could not scan array. Array not terminated by a ']' character.", NSLocalizedDescriptionKey, + NULL]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:-10 userInfo:theUserInfo]; + } + [theArray release]; + return(NO); + } + +if (outArray != NULL) + *outArray = [[theArray copy] autorelease]; + +[theArray release]; + +return(YES); +} + +- (BOOL)scanJSONStringConstant:(NSString **)outStringConstant error:(NSError **)outError +{ +NSUInteger theScanLocation = [self scanLocation]; + +[self skipWhitespace]; // TODO - i want to remove this method. But breaks unit tests. + +NSMutableString *theString = [[NSMutableString alloc] init]; + +if ([self scanCharacter:'"'] == NO) + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Could not scan string constant. String not started by a '\"' character.", NSLocalizedDescriptionKey, + NULL]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:-11 userInfo:theUserInfo]; + } + [theString release]; + return(NO); + } + +while ([self scanCharacter:'"'] == NO) + { + NSString *theStringChunk = NULL; + if ([self scanNotQuoteCharactersIntoString:&theStringChunk]) + { + [theString appendString:theStringChunk]; + } + + 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) + { + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Could not scan string constant. Unicode character could not be decoded.", NSLocalizedDescriptionKey, + NULL]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:-12 userInfo:theUserInfo]; + } + [theString release]; + return(NO); + } + theCharacter |= (theDigit << theShift); + } + } + break; + default: + { + [self setScanLocation:theScanLocation]; + if (outError) + { + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Could not scan string constant. Unknown escape code.", NSLocalizedDescriptionKey, + NULL]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:-13 userInfo:theUserInfo]; + } + [theString release]; + return(NO); + } + break; + } + CFStringAppendCharacters((CFMutableStringRef)theString, &theCharacter, 1); + } + } + +if (outStringConstant != NULL) + *outStringConstant = [[theString copy] autorelease]; + +[theString release]; + +return(YES); +} + +- (BOOL)scanJSONNumberConstant:(NSNumber **)outNumberConstant error:(NSError **)outError +{ +NSNumber *theNumber = NULL; +if ([self scanNumber:&theNumber] == YES) + { + if (outNumberConstant != NULL) + *outNumberConstant = theNumber; + return(YES); + } +else + { + if (outError) + { + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Could not scan number constant.", NSLocalizedDescriptionKey, + NULL]; + *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:-14 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/TouchJSON/JSON/CJSONSerializer.h b/TouchJSON/JSON/CJSONSerializer.h new file mode 100644 index 0000000..b858bf1 --- /dev/null +++ b/TouchJSON/JSON/CJSONSerializer.h @@ -0,0 +1,45 @@ +// +// CJSONSerializer.h +// TouchJSON +// +// Created by Jonathan Wight on 12/07/2005. +// Copyright (c) 2005 Jonathan Wight +// +// 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; + +- (NSString *)serializeObject:(id)inObject; + +- (NSString *)serializeNull:(NSNull *)inNull; +- (NSString *)serializeNumber:(NSNumber *)inNumber; +- (NSString *)serializeString:(NSString *)inString; +- (NSString *)serializeArray:(NSArray *)inArray; +- (NSString *)serializeDictionary:(NSDictionary *)inDictionary; + +@end diff --git a/TouchJSON/JSON/CJSONSerializer.m b/TouchJSON/JSON/CJSONSerializer.m new file mode 100644 index 0000000..7511e11 --- /dev/null +++ b/TouchJSON/JSON/CJSONSerializer.m @@ -0,0 +1,184 @@ +// +// CJSONSerializer.m +// TouchJSON +// +// Created by Jonathan Wight on 12/07/2005. +// Copyright (c) 2005 Jonathan Wight +// +// 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" + +@implementation CJSONSerializer + ++ (id)serializer +{ +return([[[self alloc] init] autorelease]); +} + +- (NSString *)serializeObject:(id)inObject; +{ +NSString *theResult = @""; + +if ([inObject isKindOfClass:[NSNull class]]) + { + theResult = [self serializeNull:inObject]; + } +else if ([inObject isKindOfClass:[NSNumber class]]) + { + theResult = [self serializeNumber:inObject]; + } +else if ([inObject isKindOfClass:[NSString class]]) + { + theResult = [self serializeString:inObject]; + } +else if ([inObject isKindOfClass:[NSArray class]]) + { + theResult = [self serializeArray:inObject]; + } +else if ([inObject isKindOfClass:[NSDictionary class]]) + { + theResult = [self serializeDictionary:inObject]; + } +else if ([inObject isKindOfClass:[NSData class]]) + { + NSString *theString = [[[NSString alloc] initWithData:inObject encoding:NSUTF8StringEncoding] autorelease]; + theResult = [self serializeString:theString]; + } +else + { + [NSException raise:NSGenericException format:@"Cannot serialize data of type '%@'", NSStringFromClass([inObject class])]; + } +if (theResult == NULL) + [NSException raise:NSGenericException format:@"Could not serialize object '%@'", inObject]; +return(theResult); +} + +- (NSString *)serializeNull:(NSNull *)inNull +{ +#pragma unused (inNull) +return(@"null"); +} + +- (NSString *)serializeNumber:(NSNumber *)inNumber +{ +NSString *theResult = NULL; +switch (CFNumberGetType((CFNumberRef)inNumber)) + { + case kCFNumberCharType: + { + int theValue = [inNumber intValue]; + if (theValue == 0) + theResult = @"false"; + else if (theValue == 1) + theResult = @"true"; + else + theResult = [inNumber stringValue]; + } + break; + case kCFNumberSInt8Type: + case kCFNumberSInt16Type: + case kCFNumberSInt32Type: + case kCFNumberSInt64Type: + case kCFNumberFloat32Type: + case kCFNumberFloat64Type: + case kCFNumberShortType: + case kCFNumberIntType: + case kCFNumberLongType: + case kCFNumberLongLongType: + case kCFNumberFloatType: + case kCFNumberDoubleType: + case kCFNumberCFIndexType: + default: + theResult = [inNumber stringValue]; + break; + } +return(theResult); +} + +- (NSString *)serializeString:(NSString *)inString +{ +NSMutableString *theMutableCopy = [[inString mutableCopy] autorelease]; +[theMutableCopy replaceOccurrencesOfString:@"\\" withString:@"\\\\" options:0 range:NSMakeRange(0, [theMutableCopy length])]; +[theMutableCopy replaceOccurrencesOfString:@"\"" withString:@"\\\"" options:0 range:NSMakeRange(0, [theMutableCopy length])]; +[theMutableCopy replaceOccurrencesOfString:@"/" withString:@"\\/" options:0 range:NSMakeRange(0, [theMutableCopy length])]; +[theMutableCopy replaceOccurrencesOfString:@"\b" withString:@"\\b" options:0 range:NSMakeRange(0, [theMutableCopy length])]; +[theMutableCopy replaceOccurrencesOfString:@"\f" withString:@"\\f" options:0 range:NSMakeRange(0, [theMutableCopy length])]; +[theMutableCopy replaceOccurrencesOfString:@"\n" withString:@"\\n" options:0 range:NSMakeRange(0, [theMutableCopy length])]; +[theMutableCopy replaceOccurrencesOfString:@"\n" withString:@"\\n" options:0 range:NSMakeRange(0, [theMutableCopy length])]; +[theMutableCopy replaceOccurrencesOfString:@"\t" withString:@"\\t" options:0 range:NSMakeRange(0, [theMutableCopy length])]; +/* + case 'u': + { + theCharacter = 0; + + int theShift; + for (theShift = 12; theShift >= 0; theShift -= 4) + { + int theDigit = HexToInt([self scanCharacter]); + if (theDigit == -1) + { + [self setScanLocation:theScanLocation]; + return(NO); + } + theCharacter |= (theDigit << theShift); + } + } +*/ +return([NSString stringWithFormat:@"\"%@\"", theMutableCopy]); +} + +- (NSString *)serializeArray:(NSArray *)inArray +{ +NSMutableString *theString = [NSMutableString string]; + +NSEnumerator *theEnumerator = [inArray objectEnumerator]; +id theValue = NULL; +while ((theValue = [theEnumerator nextObject]) != NULL) + { + [theString appendString:[self serializeObject:theValue]]; + if (theValue != [inArray lastObject]) + [theString appendString:@","]; + } +return([NSString stringWithFormat:@"[%@]", theString]); +} + +- (NSString *)serializeDictionary:(NSDictionary *)inDictionary +{ +NSMutableString *theString = [NSMutableString string]; + +NSArray *theKeys = [inDictionary allKeys]; +NSEnumerator *theEnumerator = [theKeys objectEnumerator]; +NSString *theKey = NULL; +while ((theKey = [theEnumerator nextObject]) != NULL) + { + id theValue = [inDictionary objectForKey:theKey]; + + [theString appendFormat:@"%@:%@", [self serializeString:theKey], [self serializeObject:theValue]]; + if (theKey != [theKeys lastObject]) + [theString appendString:@","]; + } +return([NSString stringWithFormat:@"{%@}", theString]); +} + +@end diff --git a/background.jpg b/background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..19d5ca0360795f714816b38d4e67cdfe9df3ba98 GIT binary patch literal 6141 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 literal 0 HcmV?d00001 diff --git a/blue.png b/blue.png new file mode 100644 index 0000000000000000000000000000000000000000..d738a5caebbc1e82acd2fac6e21a2717712ba406 GIT binary patch literal 398 zcmV;90df9`P)9b+{r#wl<{{lmZZqYH<#&ZHgk^@1sD%i~y~=YDP2LmQx~alREl5wA&y( z1M?Dh-?1PuKM^to^m;W#5ljO?zciV#G +#include + +#import "ccTypes.h" + +enum { + //! Default tag + kActionTagInvalid = -1, +}; + +/** Base class for actions + */ +@interface Action : NSObject { + id target; + int tag; +} + +/** The "target". The action will modify the target properties */ +@property (nonatomic,readwrite,assign) id target; +/** The action tag. An identifier of the action */ +@property (nonatomic,readwrite,assign) int tag; + ++(id) action; +-(id) init; + +-(id) copyWithZone: (NSZone*) zone; + +//! called before the action start +-(void) start; +//! return YES if the action has finished +-(BOOL) isDone; +//! called after the action has finished +-(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 FiniteTimeAction : Action +{ + //! duration in seconds + ccTime duration; +} +//! duration in seconds of the action +@property (nonatomic,readwrite) ccTime duration; + +/** returns a reversed action */ +- (FiniteTimeAction*) reverse; +@end + + +@class IntervalAction; +/** 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 RepeatForever : Action +{ + IntervalAction *other; +} +/** creates the action */ ++(id) actionWithAction: (IntervalAction*) action; +/** initializes the action */ +-(id) initWithAction: (IntervalAction*) 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 IntervalAction + */ +@interface Speed : Action +{ + IntervalAction *other; + float speed; +} +/** alter the speed of the inner function in runtime */ +@property (nonatomic,readwrite) float speed; +/** creates the action */ ++(id) actionWithAction: (IntervalAction*) action speed:(float)rate; +/** initializes the action */ +-(id) initWithAction: (IntervalAction*) action speed:(float)rate; +@end diff --git a/cocos2d/Action.m b/cocos2d/Action.m new file mode 100644 index 0000000..fd3d2b3 --- /dev/null +++ b/cocos2d/Action.m @@ -0,0 +1,232 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import "Action.h" +#import "ccMacros.h" + +#import "IntervalAction.h" + +// +// Action Base Class +// +#pragma mark - +#pragma mark Action +@implementation Action + +@synthesize target; +@synthesize tag; + ++(id) action +{ + return [[[self alloc] init] autorelease]; +} + +-(id) init +{ + if( !(self=[super init]) ) + return nil; + + target = nil; + tag = kActionTagInvalid; + return self; +} + +-(void) dealloc +{ + CCLOG(@"deallocing %@", self); + [super dealloc]; +} + +-(NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag]; +} + +-(id) copyWithZone: (NSZone*) zone +{ + Action *copy = [[[self class] allocWithZone: zone] init]; + [copy setTarget: target]; + copy.tag = tag; + return copy; +} + +-(void) start +{ + // override me +} + +-(void) stop +{ + // override me +} + +-(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 FiniteTimeAction +@synthesize duration; + +- (FiniteTimeAction*) reverse +{ + CCLOG(@"FiniteTimeAction#reverse: Implement me"); + return nil; +} +@end + + +// +// RepeatForever +// +#pragma mark - +#pragma mark RepeatForever +@implementation RepeatForever ++(id) actionWithAction: (IntervalAction*) action +{ + return [[[self alloc] initWithAction: action] autorelease]; +} + +-(id) initWithAction: (IntervalAction*) action +{ + if( !(self=[super init]) ) + return nil; + + other = [action retain]; + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + Action *copy = [[[self class] allocWithZone: zone] initWithAction:[[other copy] autorelease] ]; + return copy; +} + +-(void) dealloc +{ + [other release]; + [super dealloc]; +} + +-(void) start +{ + [super start]; + [other setTarget: target]; + [other start]; +} + +-(void) step:(ccTime) dt +{ + [other step: dt]; + if( [other isDone] ) { + [other start]; + } +} + + +-(BOOL) isDone +{ + return NO; +} + +- (IntervalAction *) reverse +{ + return [RepeatForever actionWithAction:[other reverse]]; +} + +@end + +// +// Speed +// +#pragma mark - +#pragma mark Speed +@implementation Speed +@synthesize speed; + ++(id) actionWithAction: (IntervalAction*) action speed:(float)r +{ + return [[[self alloc] initWithAction: action speed:r] autorelease]; +} + +-(id) initWithAction: (IntervalAction*) action speed:(float)r +{ + if( !(self=[super init]) ) + return nil; + + other = [action retain]; + speed = r; + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + Action *copy = [[[self class] allocWithZone: zone] initWithAction:[[other copy] autorelease] speed:speed]; + return copy; +} + +-(void) dealloc +{ + [other release]; + [super dealloc]; +} + +-(void) start +{ + [super start]; + [other setTarget: target]; + [other start]; +} + +-(void) stop +{ + [other stop]; + [super stop]; +} + +-(void) step:(ccTime) dt +{ + [other step: dt * speed]; +} + +-(BOOL) isDone +{ + return [other isDone]; +} + +- (IntervalAction *) reverse +{ + return [Speed actionWithAction:[other reverse] speed:speed]; +} +@end + + + diff --git a/cocos2d/ActionManager.h b/cocos2d/ActionManager.h new file mode 100644 index 0000000..1532b40 --- /dev/null +++ b/cocos2d/ActionManager.h @@ -0,0 +1,92 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * Copyright (C) 2009 Valentin Milea + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "Action.h" +#import "Support/ccArray.h" +#import "Support/ccHashSet.h" + +typedef struct _hashElement +{ + struct ccArray *actions; + id target; + unsigned int actionIndex; + Action *currentAction; + BOOL currentActionSalvaged; + BOOL paused; +} tHashElement; + + +/** ActionManager 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 CocosNode 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 CocosNode. + - When you want to pause / resume the actions + + @since v0.8 + */ +@interface ActionManager : NSObject { + + ccHashSet * targets; + tHashElement * currentTarget; + BOOL currentTargetSalvaged; +} + +/** returns a shared instance of the ActionManager */ ++ (ActionManager *)sharedManager; + +// actions + +/** Adds an action with a target. The action can be added paused or unpaused. + The action will be run "against" the target. + If the action is added paused, then it will be queued, but it won't be "ticked" until it is resumed. + If the action is added unpaused, then it will be queued, and it will be "ticked" in every frame. + */ +-(void) addAction: (Action*) 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: (Action*) action; +/** Removes an action given its tag and the target */ +-(void) removeActionByTag:(int)tag target:(id)target; +/** Gets an action given its tag an a target + @return the Action the with the given tag + */ +-(Action*) getActionByTag:(int) 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. + */ +-(int) numberOfRunningActionsInTarget:(id)target; +/** Pauses all actions for a certain target. + When the actions are paused, they won't be "ticked". + */ +-(void) pauseAllActionsForTarget:(id)target; +/** Resumes all actions for a certain target. + Once the actions are resumed, they will be "ticked" in every frame. + */ +-(void) resumeAllActionsForTarget:(id)target; + +@end + diff --git a/cocos2d/ActionManager.m b/cocos2d/ActionManager.m new file mode 100644 index 0000000..eb73553 --- /dev/null +++ b/cocos2d/ActionManager.m @@ -0,0 +1,356 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * Copyright (C) 2009 Valentin Milea + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import "ActionManager.h" +#import "Scheduler.h" +#import "ccMacros.h" +#import "Support/ccHashSet.h" + + +// +// hash +// +// Equal function for targetSet. +static int +targetSetEql(void *ptr, void *elt) +{ + tHashElement *first = (tHashElement*) ptr; + tHashElement *second = (tHashElement*) elt; + return (first->target == second->target); +} + +// +// singleton stuff +// +static ActionManager *_sharedManager = nil; + +@interface ActionManager (Private) +-(void) removeActionAtIndex:(NSUInteger)index hashElement:(tHashElement*)element; +-(void) deleteHashElement:(tHashElement*)element; +-(void) actionAllocWithHashElement:(tHashElement*)element; +@end + + +@implementation ActionManager + +#pragma mark ActionManager - init ++ (ActionManager *)sharedManager +{ + @synchronized([ActionManager class]) + { + if (!_sharedManager) + [[self alloc] init]; + + return _sharedManager; + } + // to avoid compiler warning + return nil; +} + ++(id)alloc +{ + @synchronized([ActionManager class]) + { + NSAssert(_sharedManager == nil, @"Attempted to allocate a second instance of a singleton."); + _sharedManager = [super alloc]; + return _sharedManager; + } + // to avoid compiler warning + return nil; +} + +-(id) init +{ + if ((self=[super init]) ) { + [[Scheduler sharedScheduler] scheduleTimer: [Timer timerWithTarget:self selector:@selector(tick:)]]; + targets = ccHashSetNew(131, targetSetEql); + } + + return self; +} + +- (void) dealloc +{ + CCLOG( @"deallocing %@", self); + + [self removeAllActions]; + ccHashSetFree(targets); + + _sharedManager = nil; + + [super dealloc]; +} + +#pragma mark ActionManager - Private + +-(void) deleteHashElement:(tHashElement*)element +{ + ccArrayFree(element->actions); + ccHashSetRemove(targets, CC_HASH_INT(element->target), element); +// CCLOG(@"---- 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 +{ + 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) pauseAllActionsForTarget:(id)target +{ + tHashElement elementTmp; + elementTmp.target = target; + tHashElement *element = ccHashSetFind(targets, CC_HASH_INT(target), &elementTmp); + if( element ) + element->paused = YES; +// else +// CCLOG(@"pauseAllActions: Target not found"); +} +-(void) resumeAllActionsForTarget:(id)target +{ + tHashElement elementTmp; + elementTmp.target = target; + tHashElement *element = ccHashSetFind(targets, CC_HASH_INT(target), &elementTmp); + if( element ) + element->paused = NO; +// else +// CCLOG(@"resumeAllActions: Target not found"); +} + +#pragma mark ActionManager - run + +-(void) addAction:(Action*)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 elementTmp; + elementTmp.target = target; + tHashElement *element = ccHashSetFind(targets, CC_HASH_INT(target), &elementTmp); + if( ! element ) { + element = malloc( sizeof( *element ) ); + bzero(element, sizeof(*element)); + element->paused = paused; + element->target = [target retain]; + ccHashSetInsert(targets, CC_HASH_INT(target), element, nil); +// CCLOG(@"---- 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 setTarget: target]; + [action start]; +} + +#pragma mark ActionManager - remove + +-(void) removeAllActions +{ + for(int i=0; i< targets->size; i++) { + ccHashSetBin *bin; + for(bin = targets->table[i]; bin; ) { + tHashElement *elt = (tHashElement*)bin->elt; + id target = elt->target; + bin = bin->next; + [self removeAllActionsFromTarget:target]; + } + } +} +-(void) removeAllActionsFromTarget:(id)target +{ + // explicit nil handling + if( target == nil ) + return; + + tHashElement elementTmp; + elementTmp.target = target; + tHashElement *element = ccHashSetFind(targets, CC_HASH_INT(target), &elementTmp); + 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(@"removeAllActionsFromTarget: Target not found"); + } +} + +-(void) removeAction: (Action*) action +{ + // explicit nil handling + if (action == nil) + return; + + tHashElement elementTmp; + elementTmp.target = [action target]; + tHashElement *element = ccHashSetFind(targets, CC_HASH_INT(elementTmp.target), &elementTmp); + if( element ) { + NSUInteger i = ccArrayGetIndexOfObject(element->actions, action); + if( i != NSNotFound ) { + if( action == element->currentAction && !element->currentActionSalvaged ) { + [element->currentAction retain]; + element->currentActionSalvaged = YES; + } + + [self removeActionAtIndex:i hashElement:element]; + } + } else { +// CCLOG(@"removeAction: Target not found"); + } +} + +-(void) removeActionByTag:(int) aTag target:(id)target +{ + NSAssert( aTag != kActionTagInvalid, @"Invalid tag"); + NSAssert( target != nil, @"Target should be ! nil"); + + tHashElement elementTmp; + elementTmp.target = target; + tHashElement *element = ccHashSetFind(targets, CC_HASH_INT(target), &elementTmp); + + if( element ) { + NSUInteger limit = element->actions->num; + for( NSUInteger i = 0; i < limit; i++) { + Action *a = element->actions->arr[i]; + + if( a.tag == aTag && [a target]==target) + return [self removeActionAtIndex:i hashElement:element]; + } +// CCLOG(@"removeActionByTag: Action not found!"); + } else { +// CCLOG(@"removeActionByTag: Target not found!"); + } +} + +#pragma mark ActionManager - get + +-(Action*) getActionByTag:(int)aTag target:(id)target +{ + NSAssert( aTag != kActionTagInvalid, @"Invalid tag"); + + tHashElement elementTmp; + elementTmp.target = target; + tHashElement *element = ccHashSetFind(targets, CC_HASH_INT(target), &elementTmp); + + if( element ) { + if( element->actions != nil ) { + NSUInteger limit = element->actions->num; + for( NSUInteger i = 0; i < limit; i++) { + Action *a = element->actions->arr[i]; + + if( a.tag == aTag ) + return a; + } + } +// CCLOG(@"getActionByTag: Action not found"); + } else { +// CCLOG(@"getActionByTag: Target not found"); + } + return nil; +} + +-(int) numberOfRunningActionsInTarget:(id) target +{ + tHashElement elementTmp; + elementTmp.target = target; + tHashElement *element = ccHashSetFind(targets, CC_HASH_INT(target), &elementTmp); + if( element ) + return element->actions ? element->actions->num : 0; + +// CCLOG(@"numberOfRunningActionsInTarget: Target not found"); + return 0; +} + +#pragma mark ActionManager - main loop + +-(void) tick: (ccTime) dt +{ + for(int i=0; i< targets->size; i++) { + ccHashSetBin *bin; + for(bin = targets->table[i]; bin; ) { + currentTarget = (tHashElement*) bin->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]; + + Action *a = currentTarget->currentAction; + // Make currentAction nil to prevent removeAction from salvaging it. + currentTarget->currentAction = nil; + [self removeAction:a]; + } + + currentTarget->currentAction = nil; + } + } + + // bin, at this moment, is still valid + // so it is safe to ask this here (issue #490) + bin = bin->next; + + // only delete currentTarget if no actions were scheduled during the cycle (issue #481) + if( currentTargetSalvaged && currentTarget->actions->num == 0 ) + [self deleteHashElement:currentTarget]; + } + } +} +@end diff --git a/cocos2d/AtlasNode.h b/cocos2d/AtlasNode.h new file mode 100644 index 0000000..e9462c9 --- /dev/null +++ b/cocos2d/AtlasNode.h @@ -0,0 +1,78 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "TextureAtlas.h" +#import "CocosNode.h" + +/** AtlasNode is a subclass of CocosNode that implements the CocosNodeRGBA and + CocosNodeTexture protocol + + It knows how to render a TextureAtlas object. + If you are going to render a TextureAtlas consider subclassing AtlasNode (or a subclass of AtlasNode) + + All features from CocosNode are valid, plus the following features: + - opacity and RGB colors + */ +@interface AtlasNode : CocosNode { + + // texture atlas + TextureAtlas *textureAtlas_; + + // chars per row + int itemsPerRow; + // chars per column + int itemsPerColumn; + + // texture coordinate x increment + float texStepX; + // texture coordinate y increment + float texStepY; + + // width of each char + int itemWidth; + // height of each char + int itemHeight; + + // blend function + ccBlendFunc blendFunc_; + + // texture RGBA. + GLubyte opacity_; + ccColor3B color_; + BOOL opacityModifyRGB_; +} + +/** conforms to CocosNodeTexture protocol */ +@property (nonatomic,readwrite,retain) TextureAtlas *textureAtlas; + +/** conforms to CocosNodeTexture protocol */ +@property (nonatomic,readwrite) ccBlendFunc blendFunc; + +/** conforms to CocosNodeRGBA protocol */ +@property (nonatomic,readonly) GLubyte opacity; +/** conforms to CocosNodeRGBA protocol */ +@property (nonatomic,readwrite) ccColor3B color; + + +/** creates an AtlasNode with an Atlas file the width and height of each item and the quantity of items to render*/ ++(id) atlasWithTileFile:(NSString*)tile tileWidth:(int)w tileHeight:(int)h itemsToRender: (int) c; + +/** initializes an AtlasNode with an Atlas file the width and height of each item and the quantity of items to render*/ +-(id) initWithTileFile:(NSString*)tile tileWidth:(int)w tileHeight:(int)h itemsToRender: (int) c; + +/** updates the Atlas (indexed vertex array). + * Shall be overriden in subclasses + */ +-(void) updateAtlasValues; +@end diff --git a/cocos2d/AtlasNode.m b/cocos2d/AtlasNode.m new file mode 100644 index 0000000..905137e --- /dev/null +++ b/cocos2d/AtlasNode.m @@ -0,0 +1,173 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "AtlasNode.h" +#import "ccMacros.h" + + +@interface AtlasNode (Private) +-(void) calculateMaxItems; +-(void) calculateTexCoordsSteps; +-(void) updateBlendFunc; +-(void) updateOpacityModifyRGB; +@end + +@implementation AtlasNode + +@synthesize opacity=opacity_, color=color_; +@synthesize textureAtlas = textureAtlas_; +@synthesize blendFunc = blendFunc_; + +#pragma mark AtlasNode - Creation & Init ++(id) atlasWithTileFile:(NSString*)tile tileWidth:(int)w tileHeight:(int)h itemsToRender: (int) c +{ + return [[[self alloc] initWithTileFile:tile tileWidth:w tileHeight:h itemsToRender:c] autorelease]; +} + + +-(id) initWithTileFile:(NSString*)tile tileWidth:(int)w tileHeight:(int)h itemsToRender: (int) c +{ + if( (self=[super init]) ) { + + itemWidth = w; + itemHeight = h; + + opacity_ = 255; + color_ = ccWHITE; + opacityModifyRGB_ = NO; + + blendFunc_.src = CC_BLEND_SRC; + blendFunc_.dst = CC_BLEND_DST; + + // retained + self.textureAtlas = [TextureAtlas textureAtlasWithFile:tile capacity:c]; + + [self updateBlendFunc]; + [self updateOpacityModifyRGB]; + + [self calculateMaxItems]; + [self calculateTexCoordsSteps]; + } + + return self; +} + +-(void) dealloc +{ + [textureAtlas_ release]; + + [super dealloc]; +} + +#pragma mark AtlasNode - Atlas generation + +-(void) calculateMaxItems +{ + CGSize s = [[textureAtlas_ texture] contentSize]; + itemsPerColumn = s.height / itemHeight; + itemsPerRow = s.width / itemWidth; +} + +-(void) calculateTexCoordsSteps +{ + texStepX = itemWidth / (float) [[textureAtlas_ texture] pixelsWide]; + texStepY = itemHeight / (float) [[textureAtlas_ texture] pixelsHigh]; +} + +-(void) updateAtlasValues +{ + [NSException raise:@"AtlasNode:Abstract" format:@"updateAtlasValue not overriden"]; +} + +#pragma mark AtlasNode - draw +- (void) draw +{ + glEnableClientState( GL_VERTEX_ARRAY); + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + + glEnable( GL_TEXTURE_2D); + + glColor4ub( color_.r, color_.g, color_.b, opacity_); + + BOOL newBlend = NO; + if( blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST ) { + newBlend = YES; + glBlendFunc( blendFunc_.src, blendFunc_.dst ); + } + + [textureAtlas_ drawQuads]; + + if( newBlend ) + glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + + // is this chepear than saving/restoring color state ? + glColor4ub( 255, 255, 255, 255); + + glDisable( GL_TEXTURE_2D); + + glDisableClientState(GL_VERTEX_ARRAY ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); +} + +#pragma mark AtlasNode - RGBA protocol + +-(void) setRGB: (GLubyte)r :(GLubyte)g :(GLubyte)b +{ + [self setColor:ccc3(r,g,b)]; +} + +-(void) setOpacity:(GLubyte)opacity +{ + // special opacity for premultiplied textures + opacity_ = opacity; + if( opacityModifyRGB_ ) + color_.r = color_.g = color_.b = opacity_; +} +-(void) updateOpacityModifyRGB +{ + opacityModifyRGB_ = [textureAtlas_.texture hasPremultipliedAlpha]; +} +-(void) setOpacityModifyRGB:(BOOL)modify +{ + opacityModifyRGB_ = modify; +} +-(BOOL) doesOpacityModifyRGB +{ + return opacityModifyRGB_; +} + +#pragma mark AtlasNode - CocosNodeTexture protocol + +-(void) updateBlendFunc +{ + if( ! [textureAtlas_.texture hasPremultipliedAlpha] ) { + blendFunc_.src = GL_SRC_ALPHA; + blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; + } +} + +-(void) setTexture:(Texture2D*)texture +{ + textureAtlas_.texture = texture; + [self updateBlendFunc]; + [self updateOpacityModifyRGB]; +} + +-(Texture2D*) texture +{ + return textureAtlas_.texture; +} + + +@end diff --git a/cocos2d/AtlasSprite.h b/cocos2d/AtlasSprite.h new file mode 100644 index 0000000..ca5dd59 --- /dev/null +++ b/cocos2d/AtlasSprite.h @@ -0,0 +1,149 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Matt Oswald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "CocosNode.h" +#import "TextureAtlas.h" + +@class AtlasSpriteManager; +@class AtlasSpriteFrame; + +#pragma mark AltasSprite + +/** AtlasSprite is a CocosNode object that implements the CocosNodeFrames and CocosNodeRGBA protocols. + * + * AtlasSprite can be used as a replacement of Sprite. + * + * AtlasSprite has all the features from CocosNode with the following additions and limitations: + * - New features + * - It is MUCH faster than Sprite + * - supports flipX, flipY + * + * - Limitations + * - Their parent can only be an AtlasSpriteManager + * - They can't have children + * - Camera is not supported yet (eg: OrbitCamera action doesn't work) + * - GridBase actions are not supported (eg: Lens, Ripple, Twirl) + * - The Alias/Antialias property belongs to AtlasSpriteManager, so you can't individually set the aliased property. + * - The Blending function property belongs to AtlasSpriteManager, so you can't individually set the blending function property. + * - Parallax scroller is not supported, but can be simulated with a "proxy" sprite. + * + * @since v0.7.1 + */ +@interface AtlasSprite : CocosNode +{ + // weak reference + TextureAtlas *textureAtlas_; + NSUInteger atlasIndex_; + + // texture pixels + CGRect rect_; + + // texture, vertex and color info + ccV3F_C4B_T2F_Quad quad_; + + // whether or not this Sprite needs to be updated in the Atlas + BOOL dirty; + + // opacity and RGB protocol + GLubyte opacity_; + ccColor3B color_; + BOOL opacityModifyRGB_; + + // Animations that belong to the sprite + NSMutableDictionary *animations; + + // image is flipped + BOOL flipX_; + BOOL flipY_; +} + +/** whether or not the Sprite needs to be updated in the Atlas */ +@property (nonatomic,readonly) BOOL dirty; +/** the quad (tex coords, vertex coords and color) information */ +@property (nonatomic,readonly) ccV3F_C4B_T2F_Quad quad; +/** returns the altas index of the AtlasSprite */ +@property (nonatomic,readonly) NSUInteger atlasIndex; +/** returns the rect of the AtlasSprite */ +@property (nonatomic,readonly) CGRect textureRect; +/** whether or not the sprite is flipped horizontally */ +@property (nonatomic,readwrite) BOOL flipX; +/** whether or not the sprite is flipped vertically */ +@property (nonatomic,readwrite) BOOL flipY; +/** opacity: conforms to CocosNodeRGBA protocol */ +@property (nonatomic,readonly) GLubyte opacity; +/** RGB colors: conforms to CocosNodeRGBA protocol */ +@property (nonatomic,readonly) ccColor3B color; + +/** creates an AtlasSprite with an AtlasSpriteManager inidicating the Rect of the Atlas */ ++(id)spriteWithRect:(CGRect)rect spriteManager:(AtlasSpriteManager*)manager; +/** initializes an AtlasSprite with an AtlasSpriteManager indicating the rect of the Atlas */ +-(id)initWithRect:(CGRect)rect spriteManager:(AtlasSpriteManager*)manager; + +-(void)insertInAtlasAtIndex:(NSUInteger)index; +-(void)updatePosition; + +/** updates the texture rect of the AtlasSprite */ +-(void) setTextureRect:(CGRect) rect; + +@end + +#pragma mark AtlasAnimation +/** an Animation object used within Sprites to perform animations */ +@interface AtlasAnimation : NSObject +{ + NSString *name; + float delay; + NSMutableArray *frames; +} + +@property (nonatomic,readwrite,assign) NSString *name; + +/** delay between frames in seconds */ +@property (nonatomic,readwrite,assign) float delay; +/** array of frames */ +@property (nonatomic,readonly) NSMutableArray *frames; + +/** creates an AtlasAnimation with an AtlasSpriteManager, a name, delay between frames */ ++(id) animationWithName:(NSString*)name delay:(float)delay; + +/** creates an AtlasAnimation with an AtlasSpriteManager, a name, delay between frames and the AtlasSpriteFrames */ ++(id) animationWithName:(NSString*)name delay:(float)delay frames:frame1,... NS_REQUIRES_NIL_TERMINATION; + +/** initializes an Animation with an AtlasSpriteManger, a name and delay between frames */ +-(id) initWithName:(NSString*)name delay:(float)delay; + +/** initializes an AtlasAnimation with an AtlasSpriteManager, a name, and the AltasSpriteFrames */ +-(id) initWithName:(NSString*)name delay:(float)delay firstFrame:(AtlasSpriteFrame*)frame vaList:(va_list) args; + +/** adds a frame to an Animation */ +-(void) addFrameWithRect:(CGRect)rect; +@end + +#pragma mark AltasSpriteFrame +/** An AtlasSpriteFrame is an NSObject that encapsulates a CGRect. + * And a CGRect represents a frame within the AtlasSpriteManager + */ +@interface AtlasSpriteFrame : NSObject +{ + CGRect rect; +} +/** rect of the frame */ +@property (nonatomic,readwrite) CGRect rect; + +/** create an AtlasSpriteFrame with a CGRect */ ++(id) frameWithRect:(CGRect)frame; +/** initializes an AtlasSpriteFrame with a CGRect */ +-(id) initWithRect:(CGRect)frame; +@end + diff --git a/cocos2d/AtlasSprite.m b/cocos2d/AtlasSprite.m new file mode 100644 index 0000000..2d37064 --- /dev/null +++ b/cocos2d/AtlasSprite.m @@ -0,0 +1,525 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Matt Oswald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "AtlasSpriteManager.h" +#import "AtlasSprite.h" +#import "Support/CGPointExtension.h" + +#pragma mark - +#pragma mark AltasSprite + +#if 1 +#define RENDER_IN_SUBPIXEL +#else +#define RENDER_IN_SUBPIXEL(__A__) ( (int)(__A__)) +#endif + +enum { + kIndexNotInitialized = 0xffffffff, +}; + +@interface AtlasSprite (Private) +-(void)updateTextureCoords; +-(void) initAnimationDictionary; +@end + +@implementation AtlasSprite + +@synthesize dirty; +@synthesize quad = quad_; +@synthesize atlasIndex = atlasIndex_; +@synthesize textureRect = rect_; +@synthesize opacity=opacity_, color=color_; + ++(id)spriteWithRect:(CGRect)rect spriteManager:(AtlasSpriteManager*)manager +{ + return [[[self alloc] initWithRect:rect spriteManager:manager] autorelease]; +} + +-(id)initWithRect:(CGRect)rect spriteManager:(AtlasSpriteManager*)manager +{ + if( (self = [super init])) { + textureAtlas_ = [manager textureAtlas]; // weak reference. Don't release + + opacityModifyRGB_ = [[textureAtlas_ texture] hasPremultipliedAlpha]; + + atlasIndex_ = kIndexNotInitialized; + + dirty = YES; + + flipY_ = flipX_ = NO; + + // default transform anchor: center + anchorPoint_ = ccp(0.5f, 0.5f); + + // RGB and opacity + opacity_ = 255; + color_ = ccWHITE; + ccColor4B tmpColor = {255,255,255,255}; + quad_.bl.colors = tmpColor; + quad_.br.colors = tmpColor; + quad_.tl.colors = tmpColor; + quad_.tr.colors = tmpColor; + + animations = nil; // lazy alloc + + [self setTextureRect:rect]; + } + + return self; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | Rect = (%.2f,%.2f,%.2f,%.2f) | tag = %i>", [self class], self, rect_.origin.x, rect_.origin.y, rect_.size.width, rect_.size.height, tag]; +} + +- (void) dealloc +{ + [animations release]; + [super dealloc]; +} + +-(void) initAnimationDictionary +{ + animations = [[NSMutableDictionary dictionaryWithCapacity:2] retain]; +} + +-(void)setTextureRect:(CGRect) rect +{ + rect_ = rect; + + [self updateTextureCoords]; + + // Don't update Atlas if index == -1. issue #283 + if( atlasIndex_ == kIndexNotInitialized) + dirty = YES; + else + [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_]; + + if( ! CGSizeEqualToSize(rect.size, contentSize_)) { + [self setContentSize:rect.size]; + dirty = YES; + } +} + +-(void)updateTextureCoords +{ + float atlasWidth = textureAtlas_.texture.pixelsWide; + float atlasHeight = textureAtlas_.texture.pixelsHigh; + + float left = rect_.origin.x / atlasWidth; + float right = (rect_.origin.x + rect_.size.width) / atlasWidth; + float top = rect_.origin.y / atlasHeight; + float bottom = (rect_.origin.y + rect_.size.height) / atlasHeight; + + + 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)updatePosition +{ + // algorithm from pyglet ( http://www.pyglet.org ) + + // if not visible + // then everything is 0 + if( ! visible ) { + quad_.br.vertices = quad_.tl.vertices = quad_.tr.vertices = quad_.bl.vertices = (ccVertex3F){0,0,0}; + + } + + // rotation ? -> update: rotation, scale, position + else if( rotation_ ) { + float x1 = -transformAnchor_.x * scaleX_; + float y1 = -transformAnchor_.y * scaleY_; + + float x2 = x1 + rect_.size.width * scaleX_; + float y2 = y1 + rect_.size.height * scaleY_; + float x = position_.x; + float y = position_.y; + + float r = -CC_DEGREES_TO_RADIANS(rotation_); + float cr = cosf(r); + float sr = sinf(r); + float ax = x1 * cr - y1 * sr + x; + float ay = x1 * sr + y1 * cr + y; + float bx = x2 * cr - y1 * sr + x; + float by = x2 * sr + y1 * cr + y; + float cx = x2 * cr - y2 * sr + x; + float cy = x2 * sr + y2 * cr + y; + float dx = x1 * cr - y2 * sr + x; + float dy = x1 * sr + y2 * cr + 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_ }; + + } + + // scale ? -> update: scale, position + else if(scaleX_ != 1 || scaleY_ != 1) + { + float x = position_.x; + float y = position_.y; + + float x1 = (x- transformAnchor_.x * scaleX_); + float y1 = (y- transformAnchor_.y * scaleY_); + float x2 = (x1 + rect_.size.width * scaleX_); + float y2 = (y1 + rect_.size.height * scaleY_); + + quad_.bl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(x1), RENDER_IN_SUBPIXEL(y1), vertexZ_ }; + quad_.br.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(x2), RENDER_IN_SUBPIXEL(y1), vertexZ_ }; + quad_.tl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(x1), RENDER_IN_SUBPIXEL(y2), vertexZ_ }; + quad_.tr.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(x2), RENDER_IN_SUBPIXEL(y2), vertexZ_ }; + + } + + // update position + else { + float x = position_.x; + float y = position_.y; + + float x1 = (x-transformAnchor_.x); + float y1 = (y-transformAnchor_.y); + float x2 = (x1 + rect_.size.width); + float y2 = (y1 + rect_.size.height); + + quad_.bl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(x1), RENDER_IN_SUBPIXEL(y1), vertexZ_ }; + quad_.br.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(x2), RENDER_IN_SUBPIXEL(y1), vertexZ_ }; + quad_.tl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(x1), RENDER_IN_SUBPIXEL(y2), vertexZ_ }; + quad_.tr.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(x2), RENDER_IN_SUBPIXEL(y2), vertexZ_ }; + + } + + [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_]; + dirty = NO; + return; +} + +-(void)insertInAtlasAtIndex:(NSUInteger)index +{ + atlasIndex_ = index; + [textureAtlas_ insertQuad:&quad_ atIndex:atlasIndex_]; +} + +// +// CocosNode property overloads +// +#pragma mark AltasSprite - property overloads +-(void)setPosition:(CGPoint)pos +{ + [super setPosition:pos]; + dirty = YES; +} + +-(void)setRotation:(float)rot +{ + [super setRotation:rot]; + dirty = YES; +} + +-(void)setScaleX:(float) sx +{ + [super setScaleX:sx]; + dirty = YES; +} + +-(void)setScaleY:(float) sy +{ + [super setScaleY:sy]; + dirty = YES; +} + +-(void)setScale:(float) s +{ + [super setScale:s]; + dirty = YES; +} + +-(void) setVertexZ:(float)z +{ + [super setVertexZ:z]; + dirty = YES; +} + +-(void)setAnchorPoint:(CGPoint)anchor +{ + [super setAnchorPoint:anchor]; + dirty = YES; +} + +-(void)setRelativeTransformAnchor:(BOOL)relative +{ + CCLOG(@"relativeTransformAnchor is ignored in AtlasSprite"); +} + +-(void)setVisible:(BOOL)v +{ + [super setVisible:v]; + dirty = YES; +} + +-(void)setFlipX:(BOOL)b +{ + if( flipX_ != b ) { + flipX_ = b; + [self setTextureRect:rect_]; + } +} +-(BOOL) flipX +{ + return flipX_; +} + +-(void) setFlipY:(BOOL)b +{ + if( flipY_ != b ) { + flipY_ = b; + [self setTextureRect:rect_]; + } +} +-(BOOL) flipY +{ + return flipY_; +} + +// +// Composition overload +// +-(id) addChild:(CocosNode*)child z:(int)z tag:(int) aTag +{ + NSAssert(NO, @"AtlasSprite can't have children"); + return nil; +} + +// +// RGBA protocol +// +#pragma mark AtlasSprite - RGBA protocol +-(void) updateColor +{ + if( atlasIndex_ != kIndexNotInitialized) + [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_]; + else + dirty = YES; +} +-(void) setOpacity:(GLubyte) anOpacity +{ + opacity_ = anOpacity; + + // special opacity for premultiplied textures + if( opacityModifyRGB_ ) + color_.r = color_.g = color_.b = opacity_; + + 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; + + [self updateColor]; +} + +-(void) setColor:(ccColor3B)color3 +{ + color_ = color3; + 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; + + [self updateColor]; + +} + +-(void) setRGB: (GLubyte)r :(GLubyte)g :(GLubyte)b +{ + [self setColor:ccc3(r,g,b)]; +} +-(void) setOpacityModifyRGB:(BOOL)modify +{ + opacityModifyRGB_ = modify; +} +-(BOOL) doesOpacityModifyRGB +{ + return opacityModifyRGB_; +} + +// +// CocosNodeFrames protocol +// +#pragma mark AtlasSprite - CocosNodeFrames protocol +-(void) setDisplayFrame:(id)newFrame +{ + AtlasSpriteFrame *frame = (AtlasSpriteFrame*)newFrame; + CGRect rect = [frame rect]; + + [self setTextureRect: rect]; +} + +-(void) setDisplayFrame: (NSString*) animationName index:(int) frameIndex +{ + if( ! animations ) + [self initAnimationDictionary]; + + AtlasAnimation *a = [animations objectForKey: animationName]; + AtlasSpriteFrame *frame = [[a frames] objectAtIndex:frameIndex]; + + NSAssert( frame, @"AtlasSprite#setDisplayFrame. Invalid frame"); + CGRect rect = [frame rect]; + + [self setTextureRect: rect]; + +} + +-(BOOL) isFrameDisplayed:(id)frame +{ + AtlasSpriteFrame *spr = (AtlasSpriteFrame*)frame; + CGRect r = [spr rect]; + return CGRectEqualToRect(r, rect_); +} + +-(id) displayFrame +{ + return [AtlasSpriteFrame frameWithRect:rect_]; +} +// XXX: duplicated code. Sprite.m and AtlasSprite.m share this same piece of code +-(void) addAnimation: (id) anim +{ + // lazy alloc + if( ! animations ) + [self initAnimationDictionary]; + + [animations setObject:anim forKey:[anim name]]; +} +// XXX: duplicated code. Sprite.m and AtlasSprite.m share this same piece of code +-(id)animationByName: (NSString*) animationName +{ + NSAssert( animationName != nil, @"animationName parameter must be non nil"); + return [animations objectForKey:animationName]; +} +@end + + +#pragma mark - +#pragma mark AltasAnimation + +@implementation AtlasAnimation +@synthesize name, delay, frames; + ++(id) animationWithName:(NSString*)aname delay:(float)d frames:rect1,... +{ + va_list args; + va_start(args,rect1); + + id s = [[[self alloc] initWithName:aname delay:d firstFrame:rect1 vaList:args] autorelease]; + + va_end(args); + return s; +} + ++(id) animationWithName:(NSString*)aname delay:(float)d +{ + return [[[self alloc] initWithName:aname delay:d] autorelease]; +} + +-(id) initWithName:(NSString*)t delay:(float)d +{ + return [self initWithName:t delay:d firstFrame:nil vaList:nil]; +} + +/* initializes an AtlasAnimation with an AtlasSpriteManager, a name, and the frames from AtlasSpriteFrames */ +-(id) initWithName:(NSString*)t delay:(float)d firstFrame:(AtlasSpriteFrame*)frame vaList:(va_list)args +{ + if( (self=[super init]) ) { + + name = t; + frames = [[NSMutableArray array] retain]; + delay = d; + + if( frame ) { + [frames addObject:frame]; + + AtlasSpriteFrame *frame2 = va_arg(args, AtlasSpriteFrame*); + while(frame2) { + [frames addObject:frame2]; + frame2 = va_arg(args, AtlasSpriteFrame*); + } + } + } + return self; +} + +-(void) dealloc +{ + CCLOG( @"deallocing %@",self); + [frames release]; + [super dealloc]; +} + +-(void) addFrameWithRect:(CGRect)rect +{ + AtlasSpriteFrame *frame = [AtlasSpriteFrame frameWithRect:rect]; + [frames addObject:frame]; +} +@end + +#pragma mark - +#pragma mark AtlasSpriteFrame +@implementation AtlasSpriteFrame +@synthesize rect; + ++(id) frameWithRect:(CGRect)frame +{ + return [[[self alloc] initWithRect:(CGRect)frame] autorelease]; +} +-(id) initWithRect:(CGRect)frame +{ + if( ([super init]) ) { + rect = frame; + } + return self; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | Rect = (%.2f,%.2f,%.2f,%.2f)>", [self class], self, + rect.origin.x, + rect.origin.y, + rect.size.width, + rect.size.height]; +} + +- (void) dealloc +{ + CCLOG( @"deallocing %@",self); + [super dealloc]; +} +@end + diff --git a/cocos2d/AtlasSpriteManager.h b/cocos2d/AtlasSpriteManager.h new file mode 100644 index 0000000..95afdd3 --- /dev/null +++ b/cocos2d/AtlasSpriteManager.h @@ -0,0 +1,79 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Matt Oswald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "CocosNode.h" +#import "TextureAtlas.h" +#import "ccMacros.h" + +#pragma mark AtlasSpriteManager + +@class AtlasSprite; + +/** AtlasSpriteManager is the object that draws all the AtlasSprite objects + * that belongs to this Manager. Use 1 AtlasSpriteManager per TextureAtlas +* + * Limitations: + * - The only object that is accepted as child is AtlasSprite + * - It's children are all Aliased or all Antialiased. + * + * @since v0.7.1 + */ +@interface AtlasSpriteManager : CocosNode +{ + unsigned int totalSprites_; + TextureAtlas *textureAtlas_; + ccBlendFunc blendFunc_; +} + +/** returns the TextureAtlas that is used */ +@property (nonatomic,readwrite,retain) TextureAtlas * textureAtlas; + +/** conforms to CocosNodeTexture protocol */ +@property (nonatomic,readwrite) ccBlendFunc blendFunc; + +/** creates an AtlasSpriteManager with a texture2d */ ++(id)spriteManagerWithTexture:(Texture2D *)tex; +/** creates an AtlasSpriteManager with a texture2d and capacity */ ++(id)spriteManagerWithTexture:(Texture2D *)tex capacity:(NSUInteger)capacity; +/** creates an AtlasSpriteManager with a file image (.png, .jpeg, .pvr, etc). + The file will be loaded using the TextureMgr. + */ ++(id)spriteManagerWithFile:(NSString*) fileImage; +/** creates an AtlasSpriteManager with a file image (.png, .jpeg, .pvr, etc) and capacity. + The file will be loaded using the TextureMgr. +*/ ++(id)spriteManagerWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity; + +/** initializes an AtlasSpriteManager with a texture2d and capacity */ +-(id)initWithTexture:(Texture2D *)tex capacity:(NSUInteger)capacity; +/** initializes an AtlasSpriteManager with a file image (.png, .jpeg, .pvr, etc). + The file will be loaded using the TextureMgr. + */ +-(id)initWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity; + +-(NSUInteger)indexForNewChildAtZ:(int)z; + +/** creates an sprite with a rect in the AtlasSpriteManage */ +-(AtlasSprite*) createSpriteWithRect:(CGRect)rect; + +/** removes a child given a certain index. It will also cleanup the running actions depending on the cleanup parameter. + @warning Removing a child from an AtlasSpriteManager 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 an AtlasSpriteManager is very slow + */ +-(void)removeChild: (AtlasSprite *)sprite cleanup:(BOOL)doCleanup; +@end diff --git a/cocos2d/AtlasSpriteManager.m b/cocos2d/AtlasSpriteManager.m new file mode 100644 index 0000000..f317b64 --- /dev/null +++ b/cocos2d/AtlasSpriteManager.m @@ -0,0 +1,339 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Matt Oswald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "AtlasSprite.h" +#import "AtlasSpriteManager.h" +#import "Grid.h" + +const int defaultCapacity = 29; + +#pragma mark AtlasSprite + +@interface AtlasSprite (Remove) +-(void)setIndex:(int)index; +@end + +@implementation AtlasSprite (Remove) +-(void)setIndex:(int)index +{ + atlasIndex_ = index; +} +@end + +@interface AtlasSpriteManager (private) +-(void) resizeAtlas; +-(void) updateBlendFunc; +@end + +#pragma mark AtlasSpriteManager +@implementation AtlasSpriteManager + +@synthesize textureAtlas = textureAtlas_; +@synthesize blendFunc = blendFunc_; + +-(void)dealloc +{ + [textureAtlas_ release]; + + [super dealloc]; +} + +/* + * creation with Texture2D + */ ++(id)spriteManagerWithTexture:(Texture2D *)tex +{ + return [[[AtlasSpriteManager alloc] initWithTexture:tex capacity:defaultCapacity] autorelease]; +} + ++(id)spriteManagerWithTexture:(Texture2D *)tex capacity:(NSUInteger)capacity +{ + return [[[AtlasSpriteManager alloc] initWithTexture:tex capacity:capacity] autorelease]; +} + +/* + * creation with File Image + */ ++(id)spriteManagerWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity +{ + return [[[AtlasSpriteManager alloc] initWithFile:fileImage capacity:capacity] autorelease]; +} + ++(id)spriteManagerWithFile:(NSString*) imageFile +{ + return [[[AtlasSpriteManager alloc] initWithFile:imageFile capacity:defaultCapacity] autorelease]; +} + + +/* + * init with Texture2D + */ +-(id)initWithTexture:(Texture2D *)tex capacity:(NSUInteger)capacity +{ + if( (self=[super init])) { + + blendFunc_.src = CC_BLEND_SRC; + blendFunc_.dst = CC_BLEND_DST; + totalSprites_ = 0; + textureAtlas_ = [[TextureAtlas alloc] initWithTexture:tex capacity:capacity]; + + [self updateBlendFunc]; + + // no lazy alloc in this node + children = [[NSMutableArray alloc] initWithCapacity:capacity]; + } + + return self; +} + +/* + * init with FileImage + */ +-(id)initWithFile:(NSString *)fileImage capacity:(NSUInteger)capacity +{ + if( (self=[super init]) ) { + + blendFunc_.src = CC_BLEND_SRC; + blendFunc_.dst = CC_BLEND_DST; + totalSprites_ = 0; + textureAtlas_ = [[TextureAtlas alloc] initWithFile:fileImage capacity:capacity]; + + [self updateBlendFunc]; + + // no lazy alloc in this node + children = [[NSMutableArray alloc] initWithCapacity:capacity]; + } + + return self; +} + + +#pragma mark AtlasSpriteManager - 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 AtlasSprite#visit, but this + // although is less mantainable, is faster + // + if (!visible) + return; + + glPushMatrix(); + + if ( grid && grid.active) + [grid beforeDraw]; + + [self transform]; + + [self draw]; + + if ( grid && grid.active) + [grid afterDraw:self.camera]; + + glPopMatrix(); +} + +-(NSUInteger)indexForNewChildAtZ:(int)z +{ + NSUInteger index = 0; + + for( AtlasSprite *sprite in children) { + if ( sprite.zOrder > z ) { + break; + } + index++; + } + + return index; +} + +-(AtlasSprite*) createSpriteWithRect:(CGRect)rect +{ + return [AtlasSprite spriteWithRect:rect spriteManager:self]; +} + +// override addChild: +-(id) addChild:(AtlasSprite*)child z:(int)z tag:(int) aTag +{ + NSAssert( child != nil, @"Argument must be non-nil"); + NSAssert( [child isKindOfClass:[AtlasSprite class]], @"AtlasSpriteManager only supports AtlasSprites as children"); + + if(totalSprites_ == textureAtlas_.capacity) + [self resizeAtlas]; + + NSUInteger index = [self indexForNewChildAtZ:z]; + [child insertInAtlasAtIndex: index]; + + totalSprites_++; + [super addChild:child z:z tag:aTag]; + + NSUInteger count = [children count]; + index++; + for(; index < count; index++) { + AtlasSprite *sprite = (AtlasSprite *)[children objectAtIndex:index]; + NSAssert([sprite atlasIndex] == index - 1, @"AtlasSpriteManager: index failed"); + [sprite setIndex:index]; + } + + return self; +} + +// override removeChild: +-(void)removeChild: (AtlasSprite *)sprite cleanup:(BOOL)doCleanup +{ + // explicit nil handling + if (sprite == nil) + return; + // ignore non-children + if( ![children containsObject:sprite] ) + return; + + NSUInteger index= sprite.atlasIndex; + [super removeChild:sprite cleanup:doCleanup]; + + [textureAtlas_ removeQuadAtIndex:index]; + + // update all sprites beyond this one + NSUInteger count = [children count]; + for(; index < count; index++) + { + AtlasSprite *other = (AtlasSprite *)[children objectAtIndex:index]; + NSAssert([other atlasIndex] == index + 1, @"AtlasSpriteManager: index failed"); + [other setIndex:index]; + } + totalSprites_--; +} + +// override reorderChild +-(void) reorderChild:(AtlasSprite*)child z:(int)z +{ + // reorder child in the children array + [super reorderChild:child z:z]; + + + // What's the new atlas index ? + NSUInteger newAtlasIndex = 0; + for( AtlasSprite *sprite in children) { + if( [sprite isEqual:child] ) + break; + newAtlasIndex++; + } + + if( newAtlasIndex != child.atlasIndex ) { + + [textureAtlas_ insertQuadFromIndex:child.atlasIndex atIndex:newAtlasIndex]; + + // update atlas index + NSUInteger count = MAX( newAtlasIndex, child.atlasIndex); + NSUInteger index = MIN( newAtlasIndex, child.atlasIndex); + for( ; index < count+1 ; index++ ) { + AtlasSprite *sprite = (AtlasSprite *)[children objectAtIndex:index]; + [sprite setIndex: index]; + } + } +} + +-(void)removeChildAtIndex:(NSUInteger)index cleanup:(BOOL)doCleanup +{ + [self removeChild:(AtlasSprite *)[children objectAtIndex:index] cleanup:doCleanup]; +} + +-(void)removeAllChildrenWithCleanup:(BOOL)doCleanup +{ + [super removeAllChildrenWithCleanup:doCleanup]; + + totalSprites_ = 0; + [textureAtlas_ removeAllQuads]; +} + +#pragma mark AtlasSpriteManager - draw +-(void)draw +{ + for( AtlasSprite *child in children ) + { + if( child.dirty ) + [child updatePosition]; + } + + if(totalSprites_ > 0) + { + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnable(GL_TEXTURE_2D); + + BOOL newBlend = NO; + if( blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST ) { + newBlend = YES; + glBlendFunc( blendFunc_.src, blendFunc_.dst ); + } + + [textureAtlas_ drawNumberOfQuads:totalSprites_]; + + if( newBlend ) + glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + + glDisable(GL_TEXTURE_2D); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + } +} + +#pragma mark AtlasSpriteManager - private +-(void) resizeAtlas +{ + // 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_.totalQuads + 1) * 4 / 3; + + CCLOG(@"Resizing TextureAtlas capacity, from [%d] to [%d].", textureAtlas_.totalQuads, quantity); + + + if( ! [textureAtlas_ resizeCapacity:quantity] ) { + // serious problems + CCLOG(@"WARNING: Not enough memory to resize the atlas"); + NSAssert(NO,@"XXX: AltasSpriteManager#resizeAtlas SHALL handle this assert"); + } +} + +#pragma mark AtlasSpriteManager - CocosNodeTexture protocol + +-(void) updateBlendFunc +{ + if( ! [textureAtlas_.texture hasPremultipliedAlpha] ) { + blendFunc_.src = GL_SRC_ALPHA; + blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; + } +} + +-(void) setTexture:(Texture2D*)texture +{ + textureAtlas_.texture = texture; + [self updateBlendFunc]; +} + +-(Texture2D*) texture +{ + return textureAtlas_.texture; +} +@end diff --git a/cocos2d/BitmapFontAtlas.h b/cocos2d/BitmapFontAtlas.h new file mode 100644 index 0000000..849ef8f --- /dev/null +++ b/cocos2d/BitmapFontAtlas.h @@ -0,0 +1,125 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + * 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 + * + */ + +#import "AtlasNode.h" +#import "AtlasSpriteManager.h" + +/** bitmap font definition */ +typedef struct _bitmapFontDef { + //! ID of the character + unsigned char 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; +} ccBitmapFontDef; + +enum { + kBitmapFontAtlasMaxChars = 256, +}; + +/** BitmapFontConfiguration has parsed configuration of the the .fnt file + @since v0.8 + */ +@interface BitmapFontConfiguration : NSObject +{ +// XXX: Creating a public interface so that the bitmapFontArray[] is accesible +@public + // The characters building up the font + ccBitmapFontDef bitmapFontArray[kBitmapFontAtlasMaxChars]; + + // FNTConfig: Common Height + NSUInteger commonHeight; + + // values for kerning + NSMutableDictionary *kerningDictionary; +} + +/** allocates a BitmapFontConfiguration with a FNT file */ ++(id) configurationWithFNTFile:(NSString*)FNTfile; +/** initializes a BitmapFontConfiguration with a FNT file */ +-(id) initWithFNTfile:(NSString*)FNTfile; +@end + + +/** BitmapFontAtlas is a subclass of AtlasSpriteManger. + + Features: + - Treats each character like an AtlasSprite. 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 Hiero format (http://slick.cokeandcode.com/demos/hiero.jnlp) + + 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 + + BitmapFontAtlas implements the protocol CocosNodeLabel, like Label and LabelAtlas. + BitmapFontAtlas has the flexibility of Label, the speed of LabelAtlas and all the features of AtlasSprite. + If in doubt, use BitmapFontAtlas instead of LabelAtlas / Label. + + @since v0.8 + */ + +@interface BitmapFontAtlas : AtlasSpriteManager +{ + // string to render + NSString *string_; + + BitmapFontConfiguration *configuration; + + // texture RGBA + GLubyte opacity_; + ccColor3B color_; + BOOL opacityModifyRGB_; +} + +/** conforms to CocosNodeRGBA protocol */ +@property (nonatomic,readonly) GLubyte opacity; +/** conforms to CocosNodeRGBA protocol */ +@property (nonatomic,readonly) ccColor3B color; + + +/** creates a bitmap font altas with an initial string and the FNT file */ ++(id) bitmapFontAtlasWithString:(NSString*)string fntFile:(NSString*)fntFile; + +/** init a bitmap font altas 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 +*/ +BitmapFontConfiguration * FNTConfigLoadFile( NSString *file ); +/** Purges the FNT config cache + */ +void FNTConfigRemoveCache( void ); + + + diff --git a/cocos2d/BitmapFontAtlas.m b/cocos2d/BitmapFontAtlas.m new file mode 100644 index 0000000..114cd4f --- /dev/null +++ b/cocos2d/BitmapFontAtlas.m @@ -0,0 +1,461 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + * 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 this editor to generate bitmap font atlas: + * http://slick.cokeandcode.com/demos/hiero.jnlp + */ + +#import "BitmapFontAtlas.h" +#import "AtlasSprite.h" +#import "Support/FileUtils.h" +#import "Support/CGPointExtension.h" + + +#pragma mark - +#pragma mark FNTConfig Cache - free functions + +NSMutableDictionary *configurations = nil; +BitmapFontConfiguration* FNTConfigLoadFile( NSString *fntFile) +{ + BitmapFontConfiguration *ret = nil; + + if( configurations == nil ) + configurations = [[NSMutableDictionary dictionaryWithCapacity:3] retain]; + + ret = [configurations objectForKey:fntFile]; + if( ret == nil ) { + ret = [BitmapFontConfiguration configurationWithFNTFile:fntFile]; + [configurations setObject:ret forKey:fntFile]; + } + + return ret; +} + +void FNTConfigRemoveCache( void ) +{ + [configurations removeAllObjects]; +} + +#pragma mark - +#pragma mark BitmapFontConfiguration + + +@interface BitmapFontConfiguration (Private) +-(void) parseConfigFile:(NSString*)controlFile; +-(void) parseCharacterDefinition:(NSString*)line charDef:(ccBitmapFontDef*)characterDefinition; +-(void) parseCommonArguments:(NSString*)line; +-(void) parseKerningCapacity:(NSString*)line; +-(void) parseKerningEntry:(NSString*)line; +@end + +@implementation BitmapFontConfiguration + ++(id) configurationWithFNTFile:(NSString*)FNTfile +{ + return [[[self alloc] initWithFNTfile:FNTfile] autorelease]; +} + +-(id) initWithFNTfile:(NSString*)fntFile +{ + if((self=[super init])) { + [self parseConfigFile:fntFile]; + } + return self; +} + +- (void) dealloc +{ + CCLOG( @"deallocing %@", self); + [kerningDictionary release]; + [super dealloc]; +} + +- (void)parseConfigFile:(NSString*)fntFile +{ + NSString *fullpath = [FileUtils fullPathFromRelativePath:fntFile]; + NSString *contents = [NSString stringWithContentsOfFile:fullpath encoding:NSUTF8StringEncoding error:nil]; + + + // 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]) ) { + // Check to see if the start of the line is something we are interested in + if([line hasPrefix:@"common lineHeight"]) { + [self parseCommonArguments:line]; + } + if([line hasPrefix:@"chars c"]) { + // Ignore this line + } else if([line hasPrefix:@"char"]) { + // Parse the current line and create a new CharDef + ccBitmapFontDef characterDefinition; + [self parseCharacterDefinition:line charDef:&characterDefinition]; + + // Add the CharDef returned to the charArray + bitmapFontArray[ 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) 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] <= 1024, @"BitmapFontAtlas: page can't be larger than 1024x1024"); + + // scaleH. sanity check + propertyValue = [nse nextObject]; + NSAssert( [propertyValue intValue] <= 1024, @"BitmapFontAtlas: page can't be larger than 1024x1024"); + + // pages + propertyValue = [nse nextObject]; + NSAssert( [propertyValue intValue] == 1, @"BitfontAtlas: only supports 1 page"); + + // packed (ignore) What does this mean ?? +} +- (void)parseCharacterDefinition:(NSString*)line charDef:(ccBitmapFontDef*)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]; + characterDefinition->charID = [propertyValue intValue]; + // 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 +{ + 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 = [[NSMutableDictionary dictionaryWithCapacity: [propertyValue intValue]] retain]; +} + +-(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 ammount = [propertyValue intValue]; + + NSString *key = [NSString stringWithFormat:@"%d,%d", first, second]; + NSNumber *value = [NSNumber numberWithInt:ammount]; + + [kerningDictionary setObject:value forKey:key]; +} + +@end + +#pragma mark - +#pragma mark BitmapFontAtlas + +@interface BitmapFontAtlas (Private) +-(NSString*) atlasNameFromFntFile:(NSString*)fntFile; + +-(int) kerningAmmountForFirst:(unichar)first second:(unichar)second; + +@end + +@implementation BitmapFontAtlas + +@synthesize opacity=opacity_, color=color_; + +#pragma mark BitmapFontAtlas - Creation & Init ++(id) bitmapFontAtlasWithString:(NSString*)string fntFile:(NSString*)fntFile +{ + return [[[self alloc] initWithString:string fntFile:fntFile] autorelease]; +} + + +-(id) initWithString:(NSString*)theString fntFile:(NSString*)fntFile +{ + NSString *textureAtlasName = [self atlasNameFromFntFile:fntFile]; + + if ((self=[super initWithFile:textureAtlasName capacity:[theString length]])) { + + opacity_ = 255; + color_ = ccWHITE; + + contentSize_ = CGSizeZero; + + opacityModifyRGB_ = [[textureAtlas_ texture] hasPremultipliedAlpha]; + + anchorPoint_ = ccp(0.5f, 0.5f); + + configuration = FNTConfigLoadFile(fntFile); + [configuration retain]; + [self setString:theString]; + } + + return self; +} + +-(void) dealloc +{ + [string_ release]; + [configuration release]; + [super dealloc]; +} + +// +// obtain the texture atlas image +// +-(NSString*) atlasNameFromFntFile:(NSString*)fntFile +{ + NSString *fullpath = [FileUtils fullPathFromRelativePath:fntFile]; + NSString *contents = [NSString stringWithContentsOfFile:fullpath encoding:NSUTF8StringEncoding error:nil]; + + NSArray *lines = [[NSArray alloc] initWithArray:[contents componentsSeparatedByString:@"\n"]]; + NSEnumerator *nse = [lines objectEnumerator]; + NSString *line; + NSString *propertyValue = nil; // ret value + + // Loop through all the lines in the lines array processing each one + while( (line = [nse nextObject]) ) { + // Check to see if the start of the line is something we are interested in + if([line hasPrefix:@"page id="]) { + + // 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]; + break; + } + } + // Finished with lines so release it + [lines release]; + + return [FileUtils fullPathFromRelativePath:propertyValue]; +} + +#pragma mark BitmapFontAtlas - FNT parser + + +#pragma mark BitmapFontAtlas - Atlas generation + +-(int) kerningAmmountForFirst:(unichar)first second:(unichar)second +{ + int ret = 0; + NSString *key = [NSString stringWithFormat:@"%d,%d", first, second]; + NSNumber *value = [configuration->kerningDictionary objectForKey:key]; + if(value) + ret = [value intValue]; + + return ret; +} + +-(void) createFontChars +{ + int nextFontPositionX = 0; + unichar prev = -1; + int kerningAmmount = 0; + + CGSize tmpSize = CGSizeZero; + + NSUInteger l = [string_ length]; + for(NSUInteger i=0; ibitmapFontArray[c]; + + CGRect rect = fontDef.rect; + + AtlasSprite *fontChar; + + fontChar = (AtlasSprite*) [self getChildByTag:i]; + if( ! fontChar ) { + fontChar = [AtlasSprite spriteWithRect:rect spriteManager:self]; + [self addChild:fontChar z:0 tag:i]; + } + else + [fontChar setTextureRect:rect]; + + fontChar.visible = YES; + + fontChar.position = ccp( nextFontPositionX + fontDef.xOffset + fontDef.xAdvance/2.0f, (configuration->commonHeight - fontDef.yOffset) - rect.size.height/2.0f ); + +// NSLog(@"position.y: %f", fontChar.position.y); + + // update kerning + fontChar.position = ccpAdd( fontChar.position, ccp(kerningAmmount,0)); + nextFontPositionX += configuration->bitmapFontArray[c].xAdvance + kerningAmmount; + prev = c; + + tmpSize.width += configuration->bitmapFontArray[c].xAdvance + kerningAmmount; + tmpSize.height = MAX( rect.size.height, contentSize_.height); + + // Apply label properties + [fontChar setOpacityModifyRGB:opacityModifyRGB_]; + // Color MUST be set before opacity, since opacity might change color if OpacityModifyRGB is on + [fontChar setColor:color_]; + [fontChar setOpacity: opacity_]; + } + + [self setContentSize:tmpSize]; +} + +#pragma mark BitmapFontAtlas - CocosNodeLabel protocol +- (void) setString:(NSString*) newString +{ + [string_ release]; + string_ = [newString retain]; + + for( CocosNode *child in children ) + child.visible = NO; + + [self createFontChars]; +} + +#pragma mark BitmapFontAtlas - CocosNodeRGBA protocol + +-(void) setColor:(ccColor3B)color +{ + color_ = color; + for( AtlasSprite* child in children ) + [child setColor:color_]; +} + +-(void) setRGB: (GLubyte)r :(GLubyte)g :(GLubyte)b +{ + [self setColor:ccc3(r,g,b)]; +} + +-(void) setOpacity:(GLubyte)opacity +{ + opacity_ = opacity; + + for( id child in children ) + [child setOpacity:opacity_]; +} +-(void) setOpacityModifyRGB:(BOOL)modify +{ + opacityModifyRGB_ = modify; + for( id child in children) + [child setOpacityModifyRGB:modify]; +} +-(BOOL) doesOpacityModifyRGB +{ + return opacityModifyRGB_; +} + +#pragma mark BitmapFontAtlas - AnchorPoint +-(void) setAnchorPoint:(CGPoint)point +{ + if( ! CGPointEqualToPoint(point, anchorPoint_) ) { + [super setAnchorPoint:point]; + [self createFontChars]; + } +} +@end diff --git a/cocos2d/Camera.h b/cocos2d/Camera.h new file mode 100644 index 0000000..afcac1e --- /dev/null +++ b/cocos2d/Camera.h @@ -0,0 +1,70 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import + +#import "CocosNode.h" + +/** + A Camera is used in every CocosNode. + 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. +*/ + +@interface Camera : 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 */ +-(void) setEyeX: (float)x eyeY:(float)y eyeZ:(float)z; +/** sets the center values */ +-(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 */ +-(void) eyeX:(float*)x eyeY:(float*)y eyeZ:(float*)z; +/** get the center vector values */ +-(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/cocos2d/Camera.m b/cocos2d/Camera.m new file mode 100644 index 0000000..1b75362 --- /dev/null +++ b/cocos2d/Camera.m @@ -0,0 +1,157 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import "Director.h" +#import "Camera.h" +#import "ccMacros.h" + +#import "Support/glu.h" + +@implementation Camera + +@synthesize 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 +{ + CCLOG(@"deallocing %@", self); + [super dealloc]; +} + +-(void) restore +{ + CGSize s = [[Director sharedDirector] displaySize]; + + eyeX = s.width/2; + eyeY = s.height/2; + eyeZ = [Camera getZEye]; + + centerX = s.width/2; + centerY = s.height/2; + centerZ = 0.0f; + + upX = 0.0f; + upY = 1.0f; + upZ = 0.0f; + + dirty = NO; +} + +-(void) locate +{ + if( dirty ) { + ccDeviceOrientation orientation = [[Director sharedDirector] deviceOrientation]; + + glLoadIdentity(); + + switch( orientation ) { + case CCDeviceOrientationPortrait: + break; + case CCDeviceOrientationPortraitUpsideDown: + glRotatef(-180,0,0,1); + break; + case CCDeviceOrientationLandscapeLeft: + glRotatef(-90,0,0,1); + break; + case CCDeviceOrientationLandscapeRight: + glRotatef(90,0,0,1); + break; + } + + gluLookAt( eyeX, eyeY, eyeZ, + centerX, centerY, centerZ, + upX, upY, upZ + ); + + switch( orientation ) { + case CCDeviceOrientationPortrait: + case CCDeviceOrientationPortraitUpsideDown: + // none + break; + case CCDeviceOrientationLandscapeLeft: + glTranslatef(-80,80,0); + break; + case CCDeviceOrientationLandscapeRight: + glTranslatef(-80,80,0); + break; + } + } +} + ++(float) getZEye +{ + CGSize s = [[Director sharedDirector] displaySize]; + return ( s.height / 1.1566f ); +} + +-(void) setEyeX: (float)x eyeY:(float)y eyeZ:(float)z +{ + eyeX = x; + eyeY = y; + eyeZ = z; + dirty = YES; +} + +-(void) setCenterX: (float)x centerY:(float)y centerZ:(float)z +{ + centerX = x; + centerY = y; + centerZ = z; + 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; + *y = eyeY; + *z = eyeZ; +} + +-(void) centerX: (float*)x centerY:(float*)y centerZ:(float*)z +{ + *x = centerX; + *y = centerY; + *z = centerZ; +} + +-(void) upX: (float*)x upY:(float*)y upZ:(float*)z +{ + *x = upX; + *y = upY; + *z = upZ; +} + +@end diff --git a/cocos2d/CameraAction.h b/cocos2d/CameraAction.h new file mode 100644 index 0000000..7a4a3f6 --- /dev/null +++ b/cocos2d/CameraAction.h @@ -0,0 +1,57 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "IntervalAction.h" + +/** Base class for Camera actions + */ +@interface CameraAction : IntervalAction { + float centerXOrig; + float centerYOrig; + float centerZOrig; + + float eyeXOrig; + float eyeYOrig; + float eyeZOrig; + + float upXOrig; + float upYOrig; + float upZOrig; +} +@end + +/** Orbit Camera action + Orbits the camera around the center of the screen using spherical coordinates + */ +@interface OrbitCamera : CameraAction { + float radius; + float deltaRadius; + float angleZ; + float deltaAngleZ; + float angleX; + float deltaAngleX; + + float radZ; + float radDeltaZ; + float radX; + float radDeltaX; + +} +/** creates an OrbitCamera 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 an OrbitCamera 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/cocos2d/CameraAction.m b/cocos2d/CameraAction.m new file mode 100644 index 0000000..f971352 --- /dev/null +++ b/cocos2d/CameraAction.m @@ -0,0 +1,127 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import "CameraAction.h" +#import "CocosNode.h" +#import "Camera.h" +#import "ccMacros.h" + +// +// CameraAction +// +@implementation CameraAction +-(void) start +{ + [super start]; + [[target camera] centerX:¢erXOrig centerY:¢erYOrig centerZ: ¢erZOrig]; + [[target camera] eyeX:&eyeXOrig eyeY:&eyeYOrig eyeZ: &eyeZOrig]; + [[target camera] upX:&upXOrig upY:&upYOrig upZ: &upZOrig]; +} + +-(id) reverse +{ + return [ReverseTime actionWithAction:self]; +} +@end + +@implementation OrbitCamera ++(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]) ) + return nil; + + 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) start +{ + [super start]; + float r, zenith, azimuth; + + [self sphericalRadius: &r zenith:&zenith azimuth:&azimuth]; + 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); + + radZ = (CGFloat)CC_DEGREES_TO_RADIANS(angleZ); + radX = (CGFloat)CC_DEGREES_TO_RADIANS(angleX); +} + +-(void) update: (ccTime) t +{ + float r = (radius + deltaRadius * t) *[Camera getZEye]; + float za = radZ + radDeltaZ * t; + float xa = radX + radDeltaX * t; + + 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; + + [[target camera] eyeX:&ex eyeY:&ey eyeZ:&ez]; + [[target camera] centerX:&cx centerY:&cy centerZ:&cz]; + + x = ex-cx; + y = ey-cy; + z = ez-cz; + + r = sqrtf( powf(x,2) + powf(y,2) + powf(z,2)); + s = sqrtf( powf(x,2) + powf(y,2)); + if(s==0.0f) + s=0.00000001f; + if(r==0.0f) + r=0.00000001f; + + *zenith = acosf( z/r); + if( x < 0 ) + *azimuth= (CGFloat)M_PI - asinf(y/s); + else + *azimuth = asinf(y/s); + + *newRadius = r / [Camera getZEye]; +} +@end diff --git a/cocos2d/CocosNode.h b/cocos2d/CocosNode.h new file mode 100644 index 0000000..f35339d --- /dev/null +++ b/cocos2d/CocosNode.h @@ -0,0 +1,489 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * Copyright (C) 2009 Valentin Milea + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import + +#import "Action.h" +#import "ccTypes.h" +#import "Support/Texture2D.h" + +enum { + kCocosNodeTagInvalid = -1, +}; + +@class Camera; +@class GridBase; + +/** CocosNode is the main element. Anything thats gets drawn or contains things that get drawn is a CocosNode. + The most popular CocosNodes are: Scene, Layer, Sprite, Menu. + + The main features of a CocosNode are: + - They can contain other cocos nodes (addChild, getChildByTag, removeChild, etc) + - They can schedule periodic callback (schedule, unschedule, etc) + - They can execute actions (runAction, stopAction, etc) + + Some CocosNodes provide extra functionality for them or their children. + + Subclassing a CocosNode 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 CocosNode: + - position + - scale (x, y) + - rotation (in degrees) + - Camera ( using spherical coordinates ) + - GridBase (to do mesh transformations) + - anchor point + - size + - visible + - z-order + - openGL z position + + Limitations: + - A CocosNode is a "void" object. It doesn't have a texture + - Since it has no texture, is has no size + - It can't receive touches + - It can't receive accelerometer values + */ +@interface CocosNode : NSObject { + + // rotation angle + float rotation_; + + // scaling factors + float scaleX_, scaleY_; + + // position of the node + CGPoint position_; + + // 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 relativeAnchorPoint_; + + // transformation anchor point + CGPoint transformAnchor_; + + // anchor point + CGPoint anchorPoint_; + // untransformed size of the node + CGSize contentSize_; + + CGAffineTransform transform_, inverse_; + BOOL isTransformDirty_, isInverseDirty_; + + // openGL real Z vertex + float vertexZ_; + + // is visible + BOOL visible; + + // a Camera + Camera *camera; + + // a Grid + GridBase *grid; + + // z-order value + int zOrder; + + // array of children + NSMutableArray *children; + + // is running + BOOL isRunning; + + // weakref to parent + CocosNode *parent; + + // a tag. any number you want to assign to the node + int tag; + + // scheduled selectors + NSMutableDictionary *scheduledSelectors; + + // user data field + void *userData; +} + +/** The z order of the node relative to it's "brothers": children of the same parent */ +@property(nonatomic,readonly) int 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 */ +@property(nonatomic,readwrite,assign) float rotation; +/** The scale factor of the node. 1.0 is the default scale factor */ +@property(nonatomic,readwrite,assign) float scale, scaleX, scaleY; +/** Position (x,y) of the node in OpenGL coordinates. (0,0) is the left-bottom corner */ +@property(nonatomic,readwrite,assign) CGPoint position; +/** A Camera object that lets you move the node using camera coordinates. + * If you use the Camera then position, scale & rotation won't be used */ +@property(nonatomic,readonly) Camera* camera; +/** A Grid object that is used when applying Effects */ +@property(nonatomic,readwrite,retain) GridBase* grid; +/** Whether of not the node is visible. Default is YES */ +@property(nonatomic,readwrite,assign) BOOL visible; +/** The transformation anchor point in absolute pixels. + since v0.8 you can only read it. If you wish to modify it, use anchorPoint instead + */ +@property(nonatomic,readonly) CGPoint transformAnchor; +/** The normalized coordinates of the anchor point. + Anchor point. (0,0) means bottom-left corner, (1,1) means top-right corner, (0.5, 0.5) means the center. + Sprites and other "textured" Nodes have a default anchorPoint of (0.5f, 0.5f) + @since v0.8 + */ +@property(nonatomic,readwrite) CGPoint anchorPoint; +/** The untransformed size of the node. + 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; +/** A weak reference to the parent */ +@property(nonatomic,readwrite,assign) CocosNode* 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 relativeAnchorPoint; +/** A tag used to identify the node easily */ +@property(nonatomic,readwrite,assign) int 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 CocosNode enters the 'stage' + If the CocosNode enters the 'stage' with a transition, this callback is called when the transition starts. + */ +-(void) onEnter; +/** callback that is called when the CocosNode enters in the 'stage'. + If the CocosNode 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 CocosNode leaves the 'stage'. + If the CocosNode leaves the 'stage' with a transition, this callback is called when the transition finishes. + */ +-(void) onExit; + + +// composition: ADD + +/** Adds a child to the container with z-order as 0. + It returns self, so you can chain several addChilds. + @since v0.7.1 + */ +-(id) addChild: (CocosNode*)node; + +/** Adds a child to the container with a z-order + It returns self, so you can chain several addChilds. + @since v0.7.1 + */ +-(id) addChild: (CocosNode*)node z:(int)z; + +/** Adds a child to the container with z order and tag + It returns self, so you can chain several addChilds. + @since v0.7.1 + */ +-(id) addChild: (CocosNode*)node z:(int)z tag:(int)tag; + +// composition: REMOVE + +/** Removes a child from the container. It will also cleanup all running actions depending on the cleanup parameter. + @since v0.7.1 + */ +-(void) removeChild: (CocosNode*)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:(int) 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 CocosNode object + @since v0.7.1 + */ +-(CocosNode*) getChildByTag:(int) tag; + +/** Returns the array that contains all the children */ +- (NSArray *)children; + +/** Reorders a child according to a new z value. + * The child MUST be already added. + */ +-(void) reorderChild:(CocosNode*)child z:(int)zOrder; + +/** Stops all running actions and schedulers + @since v0.8 + */ +-(void) cleanup; + +// draw + +/** override this method to draw your own 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; + + +// 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 + */ +-(Action*) runAction: (Action*) action; +/** Removes all actions from the running action list */ +-(void) stopAllActions; +/** Removes an action from the running action list */ +-(void) stopAction: (Action*) action; +/** Removes an action from the running action list given its tag + @since v0.7.1 +*/ +-(void) stopActionByTag:(int) tag; +/** Gets an action from the running action list given its tag + @since v0.7.1 + @return the Action the with the given tag + */ +-(Action*) getActionByTag:(int) 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. + */ +-(int) numberOfRunningActions; + +// timers + +/** check whether a selector is scheduled. */ +//-(BOOL) isScheduled: (SEL) selector; + +/** schedules a selector. + The scheduled selector will be ticked every frame + */ +-(void) schedule: (SEL) s; +/** schedules a selector with an interval time in seconds. + If time is 0 it will be ticked every frame. + */ +-(void) schedule: (SEL) s interval:(ccTime)seconds; +/** unschedule a selector */ +-(void) unschedule: (SEL) s; +/** activate all scheduled timers. + Called internally by onEnter + */ +-(void) activateTimers; +/** deactivate all scheduled timers. + Called internally by onExit + */ +-(void) deactivateTimers; + +// transformation methods + +/** actual affine transforms used + @todo nodeToParentTransform needs documentation + @since v0.7.1 + */ +- (CGAffineTransform)nodeToParentTransform; +/** @todo parentToNodeTransform needs documentation + @since v0.7.1 + */ +- (CGAffineTransform)parentToNodeTransform; +/** @todo nodeToWorldTransform needs documentation + @since v0.7.1 + */ +- (CGAffineTransform)nodeToWorldTransform; +/** @todo worldToNodeTransform needs documentation + @since v0.7.1 + */ +- (CGAffineTransform)worldToNodeTransform; +/** converts a world coordinate to local coordinate + @since v0.7.1 + */ +- (CGPoint)convertToNodeSpace:(CGPoint)worldPoint; +/** converts local coordinate to world space + @since v0.7.1 + */ +- (CGPoint)convertToWorldSpace:(CGPoint)nodePoint; +/** converts a world coordinate to local coordinate + treating the returned/received node point as anchor relative + @since v0.7.1 + */ +- (CGPoint)convertToNodeSpaceAR:(CGPoint)worldPoint; +/** converts local coordinate to world space + treating the returned/received node point as anchor relative + @since v0.7.1 + */ +- (CGPoint)convertToWorldSpaceAR:(CGPoint)nodePoint; +/** convenience methods which take a UITouch instead of CGPoint + @todo convertTouchToNodeSpace needs documentation + @since v0.7.1 + */ +- (CGPoint)convertTouchToNodeSpace:(UITouch *)touch; +/** @todo convertTouchToNodeSpaceAR needs documentation + @since v0.7.1 + */ +- (CGPoint)convertTouchToNodeSpaceAR:(UITouch *)touch; +@end + +// +// protocols +// + +/// CocosNode RGBA protocol +@protocol CocosNodeRGBA +/** 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 + */ +-(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; +/** set the color of the node + * example: [node setRGB: 255:128:24]; or [node setRGB:0xff:0x88:0x22]; + @since v0.7.1 + @deprecated Will be removed in v0.9. Use setColor instead. + */ +-(void) setRGB: (GLubyte)r :(GLubyte)g :(GLubyte)b __attribute__((deprecated)); +/** The red component of the node's color + @deprecated Will be removed in v0.9. Use color instead + */ +-(GLubyte) r __attribute__((deprecated)); +/** The green component of the node's color. + @deprecated Will be removed in v0.9. Use color instead + */ +-(GLubyte) g __attribute__((deprecated)); +/** The blue component of the node's color. + @deprecated Will be removed in v0.9. Use color instead + */ +-(GLubyte) b __attribute__((deprecated)); +@end + + +/** CocosNodes 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 + */ +@protocol CocosNodeTexture +/** returns the used texture */ +-(Texture2D*) texture; +/** sets a new texture. it will be retained */ +-(void) setTexture:(Texture2D*)texture; +/** set the source blending function for the texture */ +-(void) setBlendFunc:(ccBlendFunc)blendFunc; +/** returns the blending function used for the texture */ +-(ccBlendFunc) blendFunc; +@end + +/** Common interface for Labels */ +@protocol CocosNodeLabel +/** sets a new label using an NSString */ +-(void) setString:(NSString*)label; +@end + + + +/// Objects that supports the Animation protocol +/// @since v0.7.1 +@protocol CocosAnimation +/** reaonly array with the frames */ +-(NSArray*) frames; +/** delay of the animations */ +-(float) delay; +/** name of the animation */ +-(NSString*) name; +@end + +/// Nodes supports frames protocol +/// @since v0.7.1 +@protocol CocosNodeFrames +/** sets a new display frame to the node */ +-(void) setDisplayFrame:(id)newFrame; +/** changes the display frame based on an animation and an index */ +-(void) setDisplayFrame: (NSString*) animationName index:(int) frameIndex; +/** returns the current displayed frame */ +-(BOOL) isFrameDisplayed:(id)frame; +/** returns the current displayed frame */ +-(id) displayFrame; +/** returns an Animation given it's name */ +-(id)animationByName: (NSString*) animationName; +/** adds an Animation to the Sprite */ +-(void) addAnimation: (id) animation; +@end + diff --git a/cocos2d/CocosNode.m b/cocos2d/CocosNode.m new file mode 100644 index 0000000..7a74a51 --- /dev/null +++ b/cocos2d/CocosNode.m @@ -0,0 +1,726 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * Copyright (C) 2009 Valentin Milea + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import "CocosNode.h" +#import "Camera.h" +#import "Grid.h" +#import "Scheduler.h" +#import "ccMacros.h" +#import "Director.h" +#import "ActionManager.h" +#import "Support/CGPointExtension.h" +#import "Support/ccArray.h" +#import "Support/TransformUtils.h" + + +#if 1 +#define RENDER_IN_SUBPIXEL +#else +#define RENDER_IN_SUBPIXEL (int) +#endif + +@interface CocosNode (Private) +// lazy allocs +-(void) childrenAlloc; +-(void) timerAlloc; +// helper that reorder a child +-(void) insertChild:(CocosNode*)child z:(int)z; +// used internally to alter the zOrder variable. DON'T call this method manually +-(void) _setZOrder:(int) z; +-(void) detachChild:(CocosNode *)child cleanup:(BOOL)doCleanup; +@end + +@implementation CocosNode + +@synthesize visible; +@synthesize parent; +@synthesize grid; +@synthesize zOrder; +@synthesize tag; +@synthesize vertexZ = vertexZ_; + +#pragma mark CocosNode - Transform related properties + +@synthesize rotation=rotation_, scaleX=scaleX_, scaleY=scaleY_, position=position_; +@synthesize transformAnchor=transformAnchor_, relativeAnchorPoint=relativeAnchorPoint_; +@synthesize userData; + +// getters synthesized, setters explicit +-(void) setRotation: (float)newRotation +{ + rotation_ = newRotation; + isTransformDirty_ = isInverseDirty_ = YES; +} + +-(void) setScaleX: (float)newScaleX +{ + scaleX_ = newScaleX; + isTransformDirty_ = isInverseDirty_ = YES; +} + +-(void) setScaleY: (float)newScaleY +{ + scaleY_ = newScaleY; + isTransformDirty_ = isInverseDirty_ = YES; +} + +-(void) setPosition: (CGPoint)newPosition +{ + position_ = newPosition; + isTransformDirty_ = isInverseDirty_ = YES; +} + +-(void) setTransformAnchor: (CGPoint)newTransformAnchor +{ + transformAnchor_ = newTransformAnchor; + isTransformDirty_ = isInverseDirty_ = YES; +} + +-(void) setRelativeAnchorPoint: (BOOL)newValue +{ + relativeAnchorPoint_ = newValue; + isTransformDirty_ = isInverseDirty_ = YES; +} + +-(void) setAnchorPoint:(CGPoint)point +{ + if( ! CGPointEqualToPoint(point, anchorPoint_) ) { + anchorPoint_ = point; + self.transformAnchor = ccp( contentSize_.width * anchorPoint_.x, contentSize_.height * anchorPoint_.y ); + } +} +-(CGPoint) anchorPoint +{ + return anchorPoint_; +} +-(void) setContentSize:(CGSize)size +{ + if( ! CGSizeEqualToSize(size, contentSize_) ) { + contentSize_ = size; + self.transformAnchor = ccp( contentSize_.width * anchorPoint_.x, contentSize_.height * anchorPoint_.y ); + } +} +-(CGSize) contentSize +{ + return contentSize_; +} + +-(float) scale +{ + if( scaleX_ == scaleY_) + return scaleX_; + else + [NSException raise:@"CocosNode scale:" format:@"scaleX is different from scaleY"]; + + return 0; +} + +-(void) setScale:(float) s +{ + scaleX_ = scaleY_ = s; + isTransformDirty_ = isInverseDirty_ = YES; +} + + +#pragma mark CocosNode - Init & cleanup + ++(id) node +{ + return [[[self alloc] init] autorelease]; +} + +-(id) init +{ + if ((self=[super init]) ) { + + isRunning = NO; + + rotation_ = 0.0f; + scaleX_ = scaleY_ = 1.0f; + position_ = CGPointZero; + transformAnchor_ = CGPointZero; + anchorPoint_ = CGPointZero; + contentSize_ = CGSizeZero; + + // "whole screen" objects. like Scenes and Layers, should set relativeAnchorPoint to NO + relativeAnchorPoint_ = YES; + + isTransformDirty_ = isInverseDirty_ = YES; + + + vertexZ_ = 0; + + grid = nil; + + visible = YES; + + tag = kCocosNodeTagInvalid; + + zOrder = 0; + + // lazy alloc + camera = nil; + + // children (lazy allocs) + children = nil; + + // scheduled selectors (lazy allocs) + scheduledSelectors = nil; + + // userData is always inited as nil + userData = nil; + } + + return self; +} + +- (void)cleanup +{ + // actions + [self stopAllActions]; + + // timers + [scheduledSelectors release]; + scheduledSelectors = nil; + + [children makeObjectsPerformSelector:@selector(cleanup)]; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag]; +} + +- (void) dealloc +{ + CCLOG( @"deallocing %@", self); + + // attributes + [camera release]; + + [grid release]; + + // children + + for (CocosNode *child in children) { + child.parent = nil; + [child cleanup]; + } + + [children release]; + + // timers + [scheduledSelectors release]; + + [super dealloc]; +} + +#pragma mark CocosNode Composition + +-(void) childrenAlloc +{ + children = [[NSMutableArray arrayWithCapacity:4] retain]; +} + +// camera: lazy alloc +-(Camera*) camera +{ + if( ! camera ) + camera = [[Camera alloc] init]; + + return camera; +} + +-(CocosNode*) getChildByTag:(int) aTag +{ + NSAssert( aTag != kCocosNodeTagInvalid, @"Invalid tag"); + + for( CocosNode *node in children ) { + if( node.tag == aTag ) + return node; + } + // not found + return nil; +} + +- (NSArray *)children +{ + return (NSArray *) children; +} + +/* "add" logic MUST only be on this selector + * If a class want's to extend the 'addChild' behaviour it only needs + * to override this selector + */ +-(id) addChild: (CocosNode*) child z:(int)z tag:(int) 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]; + return self; +} + +-(id) addChild: (CocosNode*) child z:(int)z +{ + NSAssert( child != nil, @"Argument must be non-nil"); + return [self addChild:child z:z tag:child.tag]; +} + +-(id) addChild: (CocosNode*) child +{ + NSAssert( child != nil, @"Argument must be non-nil"); + return [self addChild:child z:child.zOrder tag:child.tag]; +} + +/* "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: (CocosNode*)child cleanup:(BOOL)cleanup +{ + // explicit nil handling + if (child == nil) + return; + + if ( [children containsObject:child] ) + [self detachChild:child cleanup:cleanup]; +} + +-(void) removeChildByTag:(int)aTag cleanup:(BOOL)cleanup +{ + NSAssert( aTag != kCocosNodeTagInvalid, @"Invalid tag"); + + CocosNode *child = [self getChildByTag:aTag]; + + if (child == nil) + CCLOG(@"removeChildByTag: child not found!"); + else + [self removeChild:child cleanup:cleanup]; +} + +-(void) removeAllChildrenWithCleanup:(BOOL)cleanup +{ + // not using detachChild improves speed here + for (CocosNode *c in children) + { + // 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:(CocosNode *)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:(int) z +{ + zOrder = z; +} + +// helper used by reorderChild & add +-(void) insertChild:(CocosNode*) child z:(int)z +{ + int index=0; + BOOL added = NO; + for( CocosNode *a in children ) { + if ( a.zOrder > z ) { + added = YES; + [ children insertObject:child atIndex:index]; + break; + } + index++; + } + + if( ! added ) + [children addObject:child]; + + [child _setZOrder:z]; +} + +-(void) reorderChild:(CocosNode*) child z:(int)z +{ + NSAssert( child != nil, @"Child must be non-nil"); + + [child retain]; + [children removeObject:child]; + + [self insertChild:child z:z]; + + [child release]; +} + +#pragma mark CocosNode Draw + +-(void) draw +{ + // override me + // Only use this function to draw your staff. + // DON'T draw your stuff outside this method +} + +-(void) visit +{ + if (!visible) + return; + + glPushMatrix(); + + if ( grid && grid.active) { + [grid beforeDraw]; + [self transformAncestors]; + } + + [self transform]; + + for (CocosNode * child in children) { + if ( child.zOrder < 0 ) + [child visit]; + else + break; + } + + [self draw]; + + for (CocosNode * child in children) { + if ( child.zOrder >= 0 ) + [child visit]; + } + + if ( grid && grid.active) + [grid afterDraw:self.camera]; + + glPopMatrix(); +} + +#pragma mark CocosNode - Transformations + +-(void) transformAncestors +{ + if( self.parent ) { + [self.parent transformAncestors]; + [self.parent transform]; + } +} + +-(void) transform +{ + if ( !(grid && grid.active) ) + [camera locate]; + + // transformations + + // BEGIN original implementation + // + // translate + if ( relativeAnchorPoint_ && (transformAnchor_.x != 0 || transformAnchor_.y != 0 ) ) + glTranslatef( RENDER_IN_SUBPIXEL(-transformAnchor_.x), RENDER_IN_SUBPIXEL(-transformAnchor_.y), vertexZ_); + + if (transformAnchor_.x != 0 || transformAnchor_.y != 0 ) + glTranslatef( RENDER_IN_SUBPIXEL(position_.x + transformAnchor_.x), RENDER_IN_SUBPIXEL(position_.y + transformAnchor_.y), vertexZ_); + else if ( position_.x !=0 || position_.y !=0) + glTranslatef( RENDER_IN_SUBPIXEL(position_.x), RENDER_IN_SUBPIXEL(position_.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 ); + + // restore and re-position point + if (transformAnchor_.x != 0.0f || transformAnchor_.y != 0.0f) + glTranslatef(RENDER_IN_SUBPIXEL(-transformAnchor_.x), RENDER_IN_SUBPIXEL(-transformAnchor_.y), vertexZ_); + // + // END original implementation + + /* + // BEGIN alternative -- using cached transform + // + static GLfloat m[16]; + CGAffineTransform t = [self nodeToParentTransform]; + CGAffineToGL(&t, m); + glMultMatrixf(m); + glTranslatef(0, 0, vertexZ_); + // + // END alternative + */ +} + +#pragma mark CocosNode SceneManagement + +-(void) onEnter +{ + for( id child in children ) + [child onEnter]; + + [self activateTimers]; + + isRunning = YES; +} + +-(void) onEnterTransitionDidFinish +{ + for( id child in children ) + [child onEnterTransitionDidFinish]; +} + +-(void) onExit +{ + [self deactivateTimers]; + + isRunning = NO; + + for( id child in children ) + [child onExit]; +} + +#pragma mark CocosNode Actions + +-(Action*) runAction:(Action*) action +{ + NSAssert( action != nil, @"Argument must be non-nil"); + + [[ActionManager sharedManager] addAction:action target:self paused:!isRunning]; + return action; +} + +-(void) stopAllActions +{ + [[ActionManager sharedManager] removeAllActionsFromTarget:self]; +} + +-(void) stopAction: (Action*) action +{ + [[ActionManager sharedManager] removeAction:action]; +} + +-(void) stopActionByTag:(int)aTag +{ + NSAssert( aTag != kActionTagInvalid, @"Invalid tag"); + [[ActionManager sharedManager] removeActionByTag:aTag target:self]; +} + +-(Action*) getActionByTag:(int) aTag +{ + NSAssert( aTag != kActionTagInvalid, @"Invalid tag"); + + return [[ActionManager sharedManager] getActionByTag:aTag target:self]; +} + +-(int) numberOfRunningActions +{ + return [[ActionManager sharedManager] numberOfRunningActionsInTarget:self]; +} + +#pragma mark CocosNode Timers + +-(void) timerAlloc +{ + scheduledSelectors = [[NSMutableDictionary dictionaryWithCapacity: 2] retain]; +} + +-(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"); + + if( !scheduledSelectors ) + [self timerAlloc]; + + NSString *key = NSStringFromSelector(selector); + // already scheduled ? + if( [scheduledSelectors objectForKey:key ] ) { + return; + } + + Timer *timer = [Timer timerWithTarget:self selector:selector interval:interval]; + + if( isRunning ) + [[Scheduler sharedScheduler] scheduleTimer:timer]; + + [scheduledSelectors setObject:timer forKey:key ]; +} + +-(void) unschedule: (SEL) selector +{ + // explicit nil handling + if (selector == nil) + return; + + Timer *timer = nil; + NSString *key = NSStringFromSelector(selector); + + if( ! (timer = [scheduledSelectors objectForKey:key] ) ) + { + CCLOG(@"CocosNode.unschedule: Selector not scheduled: %@",key ); + return; + } + + [scheduledSelectors removeObjectForKey: key]; + + if( isRunning ) + [[Scheduler sharedScheduler] unscheduleTimer:timer]; +} + +- (void) activateTimers +{ + for( id key in scheduledSelectors ) + [[Scheduler sharedScheduler] scheduleTimer: [scheduledSelectors objectForKey:key]]; + + [[ActionManager sharedManager] resumeAllActionsForTarget:self]; +} + +- (void) deactivateTimers +{ + for( id key in scheduledSelectors ) + [[Scheduler sharedScheduler] unscheduleTimer: [scheduledSelectors objectForKey:key]]; + + [[ActionManager sharedManager] pauseAllActionsForTarget:self]; +} + + +#pragma mark CocosNode Transform + +- (CGAffineTransform)nodeToParentTransform +{ + if ( isTransformDirty_ ) { + + transform_ = CGAffineTransformIdentity; + + if ( !relativeAnchorPoint_ ) { + transform_ = CGAffineTransformTranslate(transform_, (int)transformAnchor_.x, (int)transformAnchor_.y); + } + + transform_ = CGAffineTransformTranslate(transform_, (int)position_.x, (int)position_.y); + transform_ = CGAffineTransformRotate(transform_, -CC_DEGREES_TO_RADIANS(rotation_)); + transform_ = CGAffineTransformScale(transform_, scaleX_, scaleY_); + + transform_ = CGAffineTransformTranslate(transform_, -(int)transformAnchor_.x, -(int)transformAnchor_.y); + + isTransformDirty_ = NO; + } + + return transform_; +} + +- (CGAffineTransform)parentToNodeTransform +{ + if ( isInverseDirty_ ) { + inverse_ = CGAffineTransformInvert([self nodeToParentTransform]); + isInverseDirty_ = NO; + } + + return inverse_; +} + +- (CGAffineTransform)nodeToWorldTransform +{ + CGAffineTransform t = [self nodeToParentTransform]; + + for (CocosNode *p = parent; p != nil; p = p.parent) + t = CGAffineTransformConcat(t, [p nodeToParentTransform]); + + return t; +} + +- (CGAffineTransform)worldToNodeTransform +{ + return CGAffineTransformInvert([self nodeToWorldTransform]); +} + +- (CGPoint)convertToNodeSpace:(CGPoint)worldPoint +{ + return CGPointApplyAffineTransform(worldPoint, [self worldToNodeTransform]); +} + +- (CGPoint)convertToWorldSpace:(CGPoint)nodePoint +{ + return CGPointApplyAffineTransform(nodePoint, [self nodeToWorldTransform]); +} + +- (CGPoint)convertToNodeSpaceAR:(CGPoint)worldPoint +{ + CGPoint nodePoint = [self convertToNodeSpace:worldPoint]; + return ccpSub(nodePoint, transformAnchor_); +} + +- (CGPoint)convertToWorldSpaceAR:(CGPoint)nodePoint +{ + nodePoint = ccpAdd(nodePoint, transformAnchor_); + return [self convertToWorldSpace:nodePoint]; +} + +// convenience methods which take a UITouch instead of CGPoint + +- (CGPoint)convertTouchToNodeSpace:(UITouch *)touch +{ + CGPoint point = [touch locationInView: [touch view]]; + point = [[Director sharedDirector] convertCoordinate: point]; + return [self convertToNodeSpace:point]; +} + +- (CGPoint)convertTouchToNodeSpaceAR:(UITouch *)touch +{ + CGPoint point = [touch locationInView: [touch view]]; + point = [[Director sharedDirector] convertCoordinate: point]; + return [self convertToNodeSpaceAR:point]; +} + +@end diff --git a/cocos2d/Director.h b/cocos2d/Director.h new file mode 100644 index 0000000..e0940e5 --- /dev/null +++ b/cocos2d/Director.h @@ -0,0 +1,282 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +// +#import "ccTypes.h" + +// OpenGL related +#import "Support/EAGLView.h" + +// Fast FPS display. FPS are updated 10 times per second without consuming resources +// uncomment this line to use the old method that updated +// You need to add the "fps_images.png" file to your project +#define DIRECTOR_DISPLAY_FAST_FPS 1 + +// If you want a Fast Director that dispatches the events more frequently, +// define the following line. +// Limitations: +// - The events are dispatched faster (?) +// - But it doesn't refresh the screen as fast as the "slow events" +#define DIRECTOR_FASTDIRECTOR_FAST_EVENTS 0 + +/** Possible Pixel Formats for the EAGLView */ +typedef enum { + /** RGB565 pixel format. No alpha. 16-bit */ + kPixelFormatRGB565, + /** RGBA format. 32-bit */ + kPixelFormatRGBA8888, + + kRGB565 = kPixelFormatRGB565, + kRGBA8 = kPixelFormatRGBA8888, +} tPixelFormat; + +/** Possible DepthBuffer Formats for the EAGLView */ +typedef enum { + kDepthBufferNone, + kDepthBuffer16, + kDepthBuffer24, +} tDepthBufferFormat; + +/** Possible device orientations */ +typedef enum { + /// Device oriented vertically, home button on the bottom + CCDeviceOrientationPortrait = UIDeviceOrientationPortrait, + /// Device oriented vertically, home button on the top + CCDeviceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown, + /// Device oriented horizontally, home button on the right + CCDeviceOrientationLandscapeLeft = UIDeviceOrientationLandscapeLeft, + /// Device oriented horizontally, home button on the left + CCDeviceOrientationLandscapeRight = UIDeviceOrientationLandscapeRight, +} ccDeviceOrientation; + +@class LabelAtlas; +@class Scene; + +/**Class that creates and handle the main Window and manages how +and when to execute the Scenes +*/ +@interface Director : NSObject +{ + EAGLView *openGLView_; + + // internal timer + NSTimer *animationTimer; + NSTimeInterval animationInterval; + NSTimeInterval oldAnimationInterval; + + tPixelFormat pixelFormat_; + tDepthBufferFormat depthBufferFormat_; + + /* landscape mode ? */ + BOOL landscape; + + /* orientation */ + ccDeviceOrientation deviceOrientation_; + + /* display FPS ? */ + BOOL displayFPS; + int frames; + ccTime accumDt; + ccTime frameRate; +#ifdef DIRECTOR_DISPLAY_FAST_FPS + LabelAtlas *FPSLabel; +#endif + + /* is the running scene paused */ + BOOL isPaused_; + + /* The running scene */ + Scene *runningScene_; + + /* will be the next 'runningScene' in the next frame + nextScene is a weak reference. */ + Scene *nextScene; + + /* 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_; +} + +/** The current running Scene. Director can only run one Scene at the time */ +@property (nonatomic,readonly) Scene* 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 OpenGL view */ +@property (nonatomic,readonly) EAGLView *openGLView; +/** Pixel format used to create the context */ +@property (nonatomic,readonly) tPixelFormat pixelFormat; +/** whether or not the next delta time will be zero */ +@property (nonatomic,readwrite,assign) BOOL nextDeltaTimeZero; +/** The device orientattion */ +@property (nonatomic,readwrite) ccDeviceOrientation deviceOrientation; +/** Whether or not the Director is paused */ +@property (nonatomic,readonly) BOOL isPaused; + +/** returns a shared instance of the director */ ++(Director *)sharedDirector; +/** Uses a Director that triggers the main loop as fast as it can. + * To use it, it must be called before calling any director function + * Features and Limitations: + * - Faster than "normal" director + * - Consumes more battery than the "normal" director + * - It has some issues while using UIKit objects + */ ++(void) useFastDirector; + + +// iPhone Specific + +/** change default pixel format. + Call this class method before attaching it to a UIWindow/UIView + Default pixel format: kRGB565. Supported pixel formats: kRGBA8 and kRGB565 + */ +-(void) setPixelFormat: (tPixelFormat) p; + +/** change depth buffer format. + Call this class method before attaching it to a UIWindow/UIView + Default depth buffer: 0 (none). Supported: kDepthBufferNone, kDepthBuffer16, and kDepthBuffer24 + */ +-(void) setDepthBufferFormat: (tDepthBufferFormat) db; + +// Integration with UIKit +/** detach the cocos2d view from the view/window */ +-(BOOL)detach; + +/** attach in UIWindow using the full frame */ +-(BOOL)attachInWindow:(UIWindow *)window; + +/** attach in UIView using the full frame */ +-(BOOL)attachInView:(UIView *)view; + +/** attach in UIView using the given frame */ +-(BOOL)attachInView:(UIView *)view withFrame:(CGRect)frame; + +// Landscape + +/** returns the size of the OpenGL view according to the landspace */ +- (CGSize) winSize; +/** returns the display size of the OpenGL view */ +-(CGSize) displaySize; + +/** returns whether or not the screen is in landscape mode + @deprecated Use deviceOrientation instead + */ +- (BOOL) landscape __attribute__((deprecated)); +/** sets lanscape mode + @deprecated Use setDeviceOrientation instead + */ +- (void) setLandscape: (BOOL) on __attribute__((deprecated)); + +/** converts a UIKit coordinate to an OpenGL coordinate + Useful to convert (multi) touchs coordinates to the current layout (portrait or landscape) + */ +-(CGPoint) convertCoordinate: (CGPoint) p; + +// 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:(Scene*) 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:(Scene*) 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: (Scene*) scene; + +/** Ends the execution, releases the running scene */ +-(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; + + +// OpenGL Helper + +/** enables/disables OpenGL alpha blending */ +- (void) setAlphaBlending: (BOOL) on; +/** enables/disables OpenGL depth test */ +- (void) setDepthTest: (BOOL) on; +/** enables/disables OpenGL texture 2D */ +- (void) setTexture2D: (BOOL) on; +/** sets Cocos OpenGL default projection */ +- (void) setDefaultProjection; +/** sets a 2D projection */ +-(void) set2Dprojection; +/** sets a 3D projection */ +-(void) set3Dprojection; +@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 FastDirector : Director +{ + BOOL isRunning; + + NSAutoreleasePool *autoreleasePool; +} +-(void) preMainLoop; +@end + + + diff --git a/cocos2d/Director.m b/cocos2d/Director.m new file mode 100644 index 0000000..79a9095 --- /dev/null +++ b/cocos2d/Director.m @@ -0,0 +1,911 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +/* Idea of decoupling Window from Director taken from OC3D project: http://code.google.com/p/oc3d/ + */ + +// cocos2d imports +#import "Director.h" +#import "TouchDelegateProtocol.h" +#import "Camera.h" +#import "Scheduler.h" +#import "ActionManager.h" +#import "TextureMgr.h" +#import "LabelAtlas.h" +#import "ccMacros.h" +#import "ccExceptions.h" +#import "Transition.h" +#import "Scene.h" +#import "TouchDispatcher.h" + +// support imports +#import "Support/glu.h" +#import "Support/OpenGL_Internal.h" +#import "Support/Texture2D.h" +#import "Support/CGPointExtension.h" + +#import "Layer.h" + +#define kDefaultFPS 60.0 // 60 frames per second + +extern NSString * cocos2dVersion(void); + +@interface Director (Private) +-(BOOL)isOpenGLAttached; +-(BOOL)initOpenGLViewWithView:(UIView *)view withFrame:(CGRect)rect; + +-(void) initGLDefaultValues; + +-(void) mainLoop; +-(void) setNextScene; +// rotates the screen if Landscape mode is activated +-(void) applyLandscape; +// shows the FPS in the screen +-(void) showFPS; +// calculates delta time since last time it was called +-(void) calculateDeltaTime; + + +@end + +@implementation Director + +@synthesize animationInterval; +@synthesize runningScene = runningScene_; +@synthesize displayFPS; +@synthesize openGLView=openGLView_; +@synthesize pixelFormat=pixelFormat_; +@synthesize nextDeltaTimeZero=nextDeltaTimeZero_; +@synthesize deviceOrientation=deviceOrientation_; +@synthesize isPaused=isPaused_; + +// +// singleton stuff +// +static Director *_sharedDirector = nil; + ++ (Director *)sharedDirector +{ + @synchronized([Director class]) + { + if (!_sharedDirector) + [[self alloc] init]; + + return _sharedDirector; + } + // to avoid compiler warning + return nil; +} + +// This function was created to avoid confussion for the users +// Calling [FastDirector sharedDirector] is enough, but is somewhat +// confusing since the user needs to understand what's under the hood ++ (void) useFastDirector +{ + NSAssert(_sharedDirector==nil, @"A Director was alloced. To use Fast Director this must be the first call to Director"); + [FastDirector sharedDirector]; +} + ++(id)alloc +{ + @synchronized([Director class]) + { + NSAssert(_sharedDirector == nil, @"Attempted to allocate a second instance of a singleton."); + _sharedDirector = [super alloc]; + return _sharedDirector; + } + // to avoid compiler warning + return nil; +} + +- (id) init +{ + CCLOG(@"%@", cocos2dVersion() ); + + if( (self=[super init]) ) { + + // default values + pixelFormat_ = kPixelFormatRGB565; + depthBufferFormat_ = 0; + + // scenes + runningScene_ = nil; + nextScene = nil; + + oldAnimationInterval = animationInterval = 1.0 / kDefaultFPS; + scenesStack_ = [[NSMutableArray arrayWithCapacity:10] retain]; + + // landscape + deviceOrientation_ = CCDeviceOrientationPortrait; + + // FPS + displayFPS = NO; + frames = 0; + + // paused ? + isPaused_ = NO; + } + + return self; +} + +- (void) dealloc +{ + CCLOG( @"deallocing %@", self); + +#if DIRECTOR_DISPLAY_FAST_FPS + [FPSLabel release]; +#endif + [runningScene_ release]; + [scenesStack_ release]; + + [super dealloc]; +} + +-(void) initGLDefaultValues +{ + // This method SHOULD be called only after openGLView_ was initialized + NSAssert( openGLView_, @"openGLView_ must be initialized"); + + [self setAlphaBlending: YES]; + [self setDepthTest: YES]; + [self setDefaultProjection]; + + // set other opengl default values + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + +#if DIRECTOR_DISPLAY_FAST_FPS + if (!FPSLabel) + FPSLabel = [[LabelAtlas labelAtlasWithString:@"00.0" charMapFile:@"fps_images.png" itemWidth:16 itemHeight:24 startCharMap:'.'] retain]; +#endif +} + +// +// main loop +// +- (void) mainLoop +{ + /* clear window */ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + /* calculate "global" dt */ + [self calculateDeltaTime]; + if( ! isPaused_ ) + [[Scheduler sharedScheduler] tick: dt]; + + + /* to avoid flickr, nextScene MUST be here: after tick and before draw */ + if( nextScene ) + [self setNextScene]; + + glPushMatrix(); + + [self applyLandscape]; + + /* draw the scene */ + [runningScene_ visit]; + if( displayFPS ) + [self showFPS]; + + glPopMatrix(); + + /* swap buffers */ + [openGLView_ swapBuffers]; +} + +-(void) calculateDeltaTime +{ + struct timeval now; + + if( gettimeofday( &now, NULL) != 0 ) { + CCLOG(@"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 Scene iPhone Specific + +-(void) setPixelFormat: (tPixelFormat) format +{ + if( [self isOpenGLAttached] ) { + NSException* myException = [NSException + exceptionWithName:@"DirectorAlreadyInitialized" + reason:@"Can't change the pixel format after the director was initialized" + userInfo:nil]; + @throw myException; + } + + pixelFormat_ = format; +} + +-(void) setDepthBufferFormat: (tDepthBufferFormat) format +{ + if( [self isOpenGLAttached] ) { + NSException* myException = [NSException + exceptionWithName:@"DirectorAlreadyInitialized" + reason:@"Can't change the depth buffer format after the director was initialized" + userInfo:nil]; + @throw myException; + } + + depthBufferFormat_ = format; +} + +#pragma mark Director Scene OpenGL Helper + +- (void) setDefaultProjection +{ +// [self set2Dprojection]; + [self set3Dprojection]; +} + +-(void)set2Dprojection +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrthof(0, openGLView_.frame.size.width, 0, openGLView_.frame.size.height, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +// set a 3d projection matrix +-(void)set3Dprojection +{ + glViewport(0, 0, openGLView_.frame.size.width, openGLView_.frame.size.height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(60, (GLfloat)openGLView_.frame.size.width/openGLView_.frame.size.height, 0.5f, 1500.0f); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt( openGLView_.frame.size.width/2, openGLView_.frame.size.height/2, [Camera getZEye], + openGLView_.frame.size.width/2, openGLView_.frame.size.height/2, 0, + 0.0f, 1.0f, 0.0f); +} + +- (void) setAlphaBlending: (BOOL) on +{ + if (on) { + glEnable(GL_BLEND); + glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + } else + glDisable(GL_BLEND); +} + +- (void) setTexture2D: (BOOL) on +{ + if (on) + glEnable(GL_TEXTURE_2D); + else + glDisable(GL_TEXTURE_2D); +} + +- (void) setDepthTest: (BOOL) on +{ + if (on) { + glClearDepthf(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 + +// is the view currently attached +-(BOOL)isOpenGLAttached +{ + return ([openGLView_ superview]!=nil); +} + +// detach or attach to a view or a window +-(BOOL)detach +{ + // check if the view is attached + if(![self isOpenGLAttached]) + { + // the view is not attached + NSException* myException = [NSException + exceptionWithName:kccException_OpenGLViewNotAttached + reason:@"Can't detach the OpenGL View, because it is not attached. Attach it first." + userInfo:nil]; + @throw myException; + + return NO; + } + + // remove from the superview + [openGLView_ removeFromSuperview]; + + // check if the view is not attached anymore + if(![self isOpenGLAttached]) + { + return YES; + } + + // the view is still attached + NSException* myException = [NSException + exceptionWithName:kccException_OpenGLViewCantDetach + reason:@"Can't detach the OpenGL View, it is still attached to the superview." + userInfo:nil]; + @throw myException; + + return NO; +} + +-(BOOL)attachInWindow:(UIWindow *)window +{ + if([self initOpenGLViewWithView:window withFrame:[window bounds]]) + { + return YES; + } + + return NO; +} + +-(BOOL)attachInView:(UIView *)view +{ + if([self initOpenGLViewWithView:view withFrame:[view bounds]]) + { + return YES; + } + + return NO; +} + +-(BOOL)attachInView:(UIView *)view withFrame:(CGRect)frame +{ + if([self initOpenGLViewWithView:view withFrame:frame]) + { + return YES; + } + + return NO; +} + +-(BOOL)initOpenGLViewWithView:(UIView *)view withFrame:(CGRect)rect +{ + // check if the view is not attached + if([self isOpenGLAttached]) + { + // the view is already attached + NSException* myException = [NSException + exceptionWithName:kccException_OpenGLViewAlreadyAttached + reason:@"Can't re-attach the OpenGL View, because it is already attached. Detach it first." + userInfo:nil]; + @throw myException; + + return NO; + } + + // check if the view is not initialized + if(!openGLView_) + { + // define the pixel format + NSString *pFormat = kEAGLColorFormatRGB565; + GLuint depthFormat = 0; + + if(pixelFormat_==kPixelFormatRGBA8888) + pFormat = kEAGLColorFormatRGBA8; + + if(depthBufferFormat_ == kDepthBuffer16) + depthFormat = GL_DEPTH_COMPONENT16_OES; + else if(depthBufferFormat_ == kDepthBuffer24) + depthFormat = GL_DEPTH_COMPONENT24_OES; + + // alloc and init the opengl view + openGLView_ = [[EAGLView alloc] initWithFrame:rect pixelFormat:pFormat depthFormat:depthFormat preserveBackbuffer:NO]; + + // check if the view was alloced and initialized + if(!openGLView_) + { + // the view was not created + NSException* myException = [NSException + exceptionWithName:kccException_OpenGLViewCantInit + reason:@"Could not alloc and init the OpenGL View." + userInfo:nil]; + @throw myException; + + return NO; + } + + // set autoresizing enabled when attaching the glview to another view + [openGLView_ setAutoresizesEAGLSurface:YES]; + } + else + { + // set the (new) frame of the glview + [openGLView_ setFrame:rect]; + } + + // set the touch delegate of the glview to self + [openGLView_ setTouchDelegate: [TouchDispatcher sharedDispatcher]]; + + + // check if the superview has touchs enabled and enable it in our view + if([view isUserInteractionEnabled]) + { + [openGLView_ setUserInteractionEnabled:YES]; + [[TouchDispatcher sharedDispatcher] setDispatchEvents: YES]; + } + else + { + [openGLView_ setUserInteractionEnabled:NO]; + [[TouchDispatcher sharedDispatcher] setDispatchEvents: NO]; + } + + // check if multi touches are enabled and set them + if([view isMultipleTouchEnabled]) + { + [openGLView_ setMultipleTouchEnabled:YES]; + } + else + { + [openGLView_ setMultipleTouchEnabled:NO]; + } + + // add the glview to his (new) superview + [view addSubview:openGLView_]; + + // set the background color of the glview + // [backgroundColor setOpenGLClearColor]; + + // check if the glview is attached now + if([self isOpenGLAttached]) + { + [self initGLDefaultValues]; + return YES; + } + + // the glview is not attached, but it should have been + NSException* myException = [NSException + exceptionWithName:kccException_OpenGLViewCantAttach + reason:@"Can't attach the OpenGL View." + userInfo:nil]; + @throw myException; + + return NO; +} + +#pragma mark Director Scene Landscape + +// convert a coordinate from uikit to opengl +-(CGPoint)convertCoordinate:(CGPoint)p +{ + float newY = openGLView_.frame.size.height - p.y; + float newX = openGLView_.frame.size.width -p.x; + + CGPoint ret; + switch ( deviceOrientation_) { + case CCDeviceOrientationPortrait: + ret = ccp( p.x, newY ); + break; + case CCDeviceOrientationPortraitUpsideDown: + ret = ccp(newX, p.y); + break; + case CCDeviceOrientationLandscapeLeft: + ret.x = p.y; + ret.y = p.x; + break; + case CCDeviceOrientationLandscapeRight: + ret.x = newY; + ret.y = newX; + break; + } + return ret; +} + +// get the current size of the glview +-(CGSize)winSize +{ + CGSize s = openGLView_.frame.size; + if( deviceOrientation_ == CCDeviceOrientationLandscapeLeft || deviceOrientation_ == CCDeviceOrientationLandscapeRight ) { + // swap x,y in landscape mode + s.width = openGLView_.frame.size.height; + s.height = openGLView_.frame.size.width; + } + return s; +} + +// return the current frame size +-(CGSize)displaySize +{ + return openGLView_.frame.size; +} + +- (BOOL) landscape +{ + return deviceOrientation_ == CCDeviceOrientationLandscapeLeft; +} + +- (void) setLandscape: (BOOL) on +{ + if( on ) + [self setDeviceOrientation:CCDeviceOrientationLandscapeLeft]; + else + [self setDeviceOrientation:CCDeviceOrientationPortrait]; +} + +- (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: UIInterfaceOrientationPortrait 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) applyLandscape +{ + // 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(160,240,0); + glRotatef(180,0,0,1); + glTranslatef(-160,-240,0); + break; + case CCDeviceOrientationLandscapeRight: + glTranslatef(160,240,0); + glRotatef(90,0,0,1); + glTranslatef(-240,-160,0); + break; + case CCDeviceOrientationLandscapeLeft: + glTranslatef(160,240,0); + glRotatef(-90,0,0,1); + glTranslatef(-240,-160,0); + break; + } +} + +#pragma mark Director Scene Management + +- (void)runWithScene:(Scene*) 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: (Scene*) scene +{ + NSAssert( scene != nil, @"Argument must be non-nil"); + + NSUInteger index = [scenesStack_ count]; + + [scenesStack_ replaceObjectAtIndex:index-1 withObject:scene]; + nextScene = scene; // nextScene is a weak ref +} + +- (void) pushScene: (Scene*) scene +{ + NSAssert( scene != nil, @"Argument must be non-nil"); + + [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 { + 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]; + + // don't release the event handlers + // They are needed in case the director is run again + [[TouchDispatcher sharedDispatcher] removeAllDelegates]; + + [self stopAnimation]; + [self detach]; + + // Purge all managers + [[Scheduler sharedScheduler] release]; + [[ActionManager sharedManager] release]; + [[TextureMgr sharedTextureMgr] release]; +} + +-(void) setNextScene +{ + BOOL runningIsTransition = [runningScene_ isKindOfClass:[TransitionScene class]]; + BOOL newIsTransition = [nextScene isKindOfClass:[TransitionScene class]]; + + // If it is not a transition, call onExit + if( ! newIsTransition ) + [runningScene_ onExit]; + + [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 ) { + NSException* myException = [NSException + exceptionWithName:@"GetTimeOfDay" + reason:@"GetTimeOfDay abnormal error" + userInfo:nil]; + @throw myException; + } + + isPaused_ = NO; + dt = 0; +} + +- (void)startAnimation +{ + NSAssert( animationTimer == nil, @"animationTimer must be nil. Calling startAnimation twice?"); + + if( gettimeofday( &lastUpdate, NULL) != 0 ) { + NSException* myException = [NSException + exceptionWithName:@"GetTimeOfDay" + reason:@"GetTimeOfDay abnormal error" + userInfo:nil]; + @throw myException; + } + + 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)stopAnimation +{ + [animationTimer invalidate]; + animationTimer = nil; +} + +- (void)setAnimationInterval:(NSTimeInterval)interval +{ + animationInterval = interval; + + if(animationTimer) { + [self stopAnimation]; + [self startAnimation]; + } +} + +#if DIRECTOR_DISPLAY_FAST_FPS + +// display the FPS using a LabelAtlas +// updates the FPS every frame +-(void) showFPS +{ + frames++; + accumDt += dt; + + if ( accumDt > 0.1) { + frameRate = frames/accumDt; + frames = 0; + accumDt = 0; + } + + NSString *str = [NSString stringWithFormat:@"%.1f",frameRate]; +// glTranslatef(10.0, 10.0, 0); + [FPSLabel setString:str]; + [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 > 0.3) { + frameRate = frames/accumDt; + frames = 0; + accumDt = 0; + } + + NSString *str = [NSString stringWithFormat:@"%.2f",frameRate]; + Texture2D *texture = [[Texture2D alloc] initWithString:str dimensions:CGSizeMake(100,30) alignment:UITextAlignmentLeft fontName:@"Arial" fontSize:24]; + glEnable(GL_TEXTURE_2D); + glEnableClientState( GL_VERTEX_ARRAY); + glEnableClientState( GL_TEXTURE_COORD_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); + glDisable(GL_TEXTURE_2D); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +} +#endif + +@end + +#pragma mark - +#pragma mark Director FastDirector + +@implementation FastDirector + +- (id) init +{ + if(( self = [super init] )) { + +#if DIRECTOR_FASTDIRECTOR_FAST_EVENTS + CCLOG(@"Using Fast Director with Fast Events"); +#else + CCLOG(@"Using Fast Director"); +#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 ) { + NSException* myException = [NSException + exceptionWithName:@"GetTimeOfDay" + reason:@"GetTimeOfDay abnormal error" + userInfo:nil]; + @throw myException; + } + + + isRunning = YES; + + SEL selector = @selector(preMainLoop); + NSMethodSignature* sig = [[[Director sharedDirector] class] + instanceMethodSignatureForSelector:selector]; + NSInvocation* invocation = [NSInvocation + invocationWithMethodSignature:sig]; + [invocation setTarget:[Director sharedDirector]]; + [invocation setSelector:selector]; + [invocation performSelectorOnMainThread:@selector(invokeWithTarget:) + withObject:[Director sharedDirector] waitUntilDone:NO]; +} + +-(void) preMainLoop +{ + while (isRunning) { + + NSAutoreleasePool *loopPool = [NSAutoreleasePool new]; + +#if DIRECTOR_FASTDIRECTOR_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 mainLoop]; + +#if DIRECTOR_FASTDIRECTOR_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 + diff --git a/cocos2d/DrawingPrimitives.h b/cocos2d/DrawingPrimitives.h new file mode 100644 index 0000000..76a1c1f --- /dev/null +++ b/cocos2d/DrawingPrimitives.h @@ -0,0 +1,71 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#ifndef __CC_DRAWING_PRIMITIVES_H +#define __CC_DRAWING_PRIMITIVES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + @file + Drawing OpenGL ES primitives. + - drawPoint + - drawLine + - drawPoly + - drawCircle + + You can change the color, width and other property by calling the + glColor4ub(), glLineWitdh(), 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. + */ + +#import // for CGPoint +#import // for BOOL + +/** draws a point given x and y coordinate */ +void drawPoint( CGPoint point ); + +/** draws an array of points. + @since v0.7.2 + */ +void drawPoints( CGPoint *points, unsigned int numberOfPoints ); + +/** draws a line given the origin and destination point */ +void drawLine( CGPoint origin, CGPoint destination ); + +/** draws a poligon given a pointer to CGPoint coordiantes and the number of vertices. The polygon can be closed or open + */ +void drawPoly( CGPoint *vertices, int numOfVertices, BOOL closePolygon ); + +/** draws a circle given the center, radius and number of segments. */ +void drawCircle( CGPoint center, float radius, float angle, int segments, BOOL drawLineToCenter); + +/** draws a quad bezier path + @since v0.8 + */ +void drawQuadBezier(CGPoint origin, CGPoint control, CGPoint destination, int segments); + +/** draws a cubic bezier path + @since v0.8 + */ +void drawCubicBezier(CGPoint origin, CGPoint control1, CGPoint control2, CGPoint destination, int segments); + +#ifdef __cplusplus +} +#endif + +#endif // __CC_DRAWING_PRIMITIVES_H diff --git a/cocos2d/DrawingPrimitives.m b/cocos2d/DrawingPrimitives.m new file mode 100644 index 0000000..14e5687 --- /dev/null +++ b/cocos2d/DrawingPrimitives.m @@ -0,0 +1,150 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import +#import +#import +#import + +#import "DrawingPrimitives.h" + +void drawPoint( CGPoint point ) +{ + glVertexPointer(2, GL_FLOAT, 0, &point); + glEnableClientState(GL_VERTEX_ARRAY); + + glDrawArrays(GL_POINTS, 0, 1); + + glDisableClientState(GL_VERTEX_ARRAY); +} + +void drawPoints( CGPoint *points, unsigned int numberOfPoints ) +{ + glVertexPointer(2, GL_FLOAT, 0, points); + glEnableClientState(GL_VERTEX_ARRAY); + + glDrawArrays(GL_POINTS, 0, numberOfPoints); + + glDisableClientState(GL_VERTEX_ARRAY); +} + + +void drawLine( CGPoint origin, CGPoint destination ) +{ + CGPoint vertices[2]; + + vertices[0] = origin; + vertices[1] = destination; + + glVertexPointer(2, GL_FLOAT, 0, vertices); + glEnableClientState(GL_VERTEX_ARRAY); + + glDrawArrays(GL_LINES, 0, 2); + + glDisableClientState(GL_VERTEX_ARRAY); +} + + +void drawPoly( CGPoint *poli, int points, BOOL closePolygon ) +{ + glVertexPointer(2, GL_FLOAT, 0, poli); + glEnableClientState(GL_VERTEX_ARRAY); + + if( closePolygon ) + glDrawArrays(GL_LINE_LOOP, 0, points); + else + glDrawArrays(GL_LINE_STRIP, 0, points); + + glDisableClientState(GL_VERTEX_ARRAY); +} + +void drawCircle( CGPoint center, float r, float a, int segs, BOOL drawLineToCenter) +{ + int additionalSegment = 1; + if (drawLineToCenter) + additionalSegment++; + + const float coef = 2.0f * (float)M_PI/segs; + + float *vertices = malloc( sizeof(float)*2*(segs+2)); + if( ! vertices ) + return; + + memset( vertices,0, sizeof(float)*2*(segs+2)); + + for(int i=0;i<=segs;i++) + { + float rads = i*coef; + float j = r * cosf(rads + a) + center.x; + float k = r * sinf(rads + a) + center.y; + + vertices[i*2] = j; + vertices[i*2+1] =k; + } + vertices[(segs+1)*2] = center.x; + vertices[(segs+1)*2+1] = center.y; + + glVertexPointer(2, GL_FLOAT, 0, vertices); + glEnableClientState(GL_VERTEX_ARRAY); + + glDrawArrays(GL_LINE_STRIP, 0, segs+additionalSegment); + + glDisableClientState(GL_VERTEX_ARRAY); + + free( vertices ); +} + +void drawQuadBezier(CGPoint origin, CGPoint control, CGPoint destination, int segments) +{ + CGPoint vertices[segments + 1]; + + float t = 0.0f; + for(int i = 0; i < segments; i++) + { + float x = powf(1 - t, 2) * origin.x + 2.0f * (1 - t) * t * control.x + t * t * destination.x; + float y = powf(1 - t, 2) * origin.y + 2.0f * (1 - t) * t * control.y + t * t * destination.y; + vertices[i] = CGPointMake(x, y); + t += 1.0f / segments; + } + vertices[segments] = destination; + + glVertexPointer(2, GL_FLOAT, 0, vertices); + glEnableClientState(GL_VERTEX_ARRAY); + + glDrawArrays(GL_LINE_STRIP, 0, segments + 1); + + glDisableClientState(GL_VERTEX_ARRAY); +} + +void drawCubicBezier(CGPoint origin, CGPoint control1, CGPoint control2, CGPoint destination, int segments) +{ + CGPoint vertices[segments + 1]; + + float t = 0; + for(int i = 0; i < segments; i++) + { + float x = powf(1 - t, 3) * origin.x + 3.0f * powf(1 - t, 2) * t * control1.x + 3.0f * (1 - t) * t * t * control2.x + t * t * t * destination.x; + float y = powf(1 - t, 3) * origin.y + 3.0f * powf(1 - t, 2) * t * control1.y + 3.0f * (1 - t) * t * t * control2.y + t * t * t * destination.y; + vertices[i] = CGPointMake(x, y); + t += 1.0f / segments; + } + vertices[segments] = destination; + + glVertexPointer(2, GL_FLOAT, 0, vertices); + glEnableClientState(GL_VERTEX_ARRAY); + + glDrawArrays(GL_LINE_STRIP, 0, segments + 1); + + glDisableClientState(GL_VERTEX_ARRAY); +} diff --git a/cocos2d/EaseAction.h b/cocos2d/EaseAction.h new file mode 100644 index 0000000..72f4158 --- /dev/null +++ b/cocos2d/EaseAction.h @@ -0,0 +1,72 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008, 2009 Jason Booth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "IntervalAction.h" + +/** Base class for Easing actions + */ +@interface EaseAction : IntervalAction +{ + IntervalAction * other; +} +/** creates the action */ ++(id) actionWithAction: (IntervalAction*) action; +/** initializes the action */ +-(id) initWithAction: (IntervalAction*) action; +@end + +/** Base class for Easing actions with rate parameters + */ +@interface EaseRateAction : EaseAction +{ + 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: (IntervalAction*) action rate:(float)rate; +/** Initializes the action with the inner action and the rate parameter */ +-(id) initWithAction: (IntervalAction*) action rate:(float)rate; +@end + +/** EaseIn action with a rate + */ +@interface EaseIn : EaseRateAction {} @end + +/** EaseOut action with a rate + */ +@interface EaseOut : EaseRateAction {} @end + +/** EaseInOut action with a rate + */ +@interface EaseInOut : EaseRateAction {} @end + +/** Ease Exponential In + */ +@interface EaseExponentialIn : EaseAction {} @end +/** Ease Exponential Out + */ +@interface EaseExponentialOut : EaseAction {} @end +/** Ease Exponential InOut + */ +@interface EaseExponentialInOut : EaseAction {} @end +/** Ease Sine In + */ +@interface EaseSineIn : EaseAction {} @end +/** Ease Sine Out + */ +@interface EaseSineOut : EaseAction {} @end +/** Ease Sine InOut + */ +@interface EaseSineInOut : EaseAction {} @end diff --git a/cocos2d/EaseAction.m b/cocos2d/EaseAction.m new file mode 100644 index 0000000..fa143c4 --- /dev/null +++ b/cocos2d/EaseAction.m @@ -0,0 +1,230 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008, 2009 Jason Booth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "EaseAction.h" + +// +// EaseAction +// +@implementation EaseAction + ++(id) actionWithAction: (IntervalAction*) action +{ + return [[[self alloc] initWithAction: action] autorelease ]; +} + +-(id) initWithAction: (IntervalAction*) action +{ + NSAssert( action!=nil, @"Ease: arguments must be non-nil"); + + if( !(self=[super initWithDuration: action.duration]) ) + return nil; + + other = [action retain]; + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + Action *copy = [[[self class] allocWithZone:zone] initWithAction:[[other copy] autorelease]]; + return copy; +} + +-(void) dealloc +{ + [other release]; + [super dealloc]; +} + +-(void) start +{ + [super start]; + [other setTarget: target]; + [other start]; +} + +-(void) update: (ccTime) t +{ + [other update: t]; +} + +-(IntervalAction*) reverse +{ + return [[self class] actionWithAction: [other reverse]]; +} +@end + + +// +// EaseRateAction +// +@implementation EaseRateAction +@synthesize rate; ++(id) actionWithAction: (IntervalAction*) action rate:(float)aRate +{ + return [[[self alloc] initWithAction: action rate:aRate] autorelease ]; +} + +-(id) initWithAction: (IntervalAction*) action rate:(float)aRate +{ + if( (self=[super initWithAction:action ]) ) { + self.rate = aRate; + } + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + Action *copy = [[[self class] allocWithZone:zone] initWithAction:[[other copy] autorelease] rate:rate]; + return copy; +} + +-(void) dealloc +{ + [super dealloc]; +} + +-(IntervalAction*) reverse +{ + return [[self class] actionWithAction: [other reverse] rate:1/rate]; +} +@end + +// +// EeseIn +// +@implementation EaseIn +-(void) update: (ccTime) t +{ + [other update: powf(t,rate)]; +} +@end + +// +// EaseOut +// +@implementation EaseOut +-(void) update: (ccTime) t +{ + [other update: powf(t,1/rate)]; +} +@end + +// +// EaseInOut +// +@implementation EaseInOut +-(void) update: (ccTime) t +{ + int sign =1; + int r = (int) rate; + if (r % 2 == 0) + sign = -1; + + if ((t*=2) < 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 +-(IntervalAction*) reverse +{ + return [[self class] actionWithAction: [other reverse] rate:rate]; +} + +@end + +// +// EaseExponentialIn +// +@implementation EaseExponentialIn +-(void) update: (ccTime) t +{ + [other update: (t==0) ? 0 : powf(2, 10 * (t/1 - 1)) - 1 * 0.001f]; +} +- (IntervalAction*) reverse +{ + return [EaseExponentialOut actionWithAction: [other reverse]]; +} +@end + +// +// EaseExponentialOut +// +@implementation EaseExponentialOut +-(void) update: (ccTime) t +{ + [other update: (t==1) ? 1 : (-powf(2, -10 * t/1) + 1)]; +} +- (IntervalAction*) reverse +{ + return [EaseExponentialIn actionWithAction: [other reverse]]; +} +@end + +// +// EaseExponentialInOut +// +@implementation EaseExponentialInOut +-(void) update: (ccTime) t +{ + if ((t/=0.5f) < 1) + t = 0.5f * powf(2, 10 * (t - 1)); + else + t = 0.5f * (-powf(2, -10 * --t) + 2); + [other update:t]; +} +@end + +// +// EaseSineIn +// +@implementation EaseSineIn +-(void) update: (ccTime) t +{ + [other update:-1*cosf(t * (float)M_PI_2) +1]; +} +- (IntervalAction*) reverse +{ + return [EaseSineOut actionWithAction: [other reverse]]; +} +@end + +// +// EaseSineOut +// +@implementation EaseSineOut +-(void) update: (ccTime) t +{ + [other update:sinf(t * (float)M_PI_2)]; +} +- (IntervalAction*) reverse +{ + return [EaseSineIn actionWithAction: [other reverse]]; +} +@end + +// +// EaseSineInOut +// +@implementation EaseSineInOut +-(void) update: (ccTime) t +{ + [other update:-0.5f*(cosf( (float)M_PI*t) - 1)]; +} +@end + + + diff --git a/cocos2d/Grabber.h b/cocos2d/Grabber.h new file mode 100644 index 0000000..1aeab6f --- /dev/null +++ b/cocos2d/Grabber.h @@ -0,0 +1,32 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 On-Core + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import +#import +#import + +@class Texture2D; + +/** FBO class that grabs the the contents of the screen */ +@interface Grabber : NSObject +{ + GLuint fbo; + GLint oldFBO; +} + +-(void)grab:(Texture2D*)texture; +-(void)beforeRender:(Texture2D*)texture; +-(void)afterRender:(Texture2D*)texture; + +@end diff --git a/cocos2d/Grabber.m b/cocos2d/Grabber.m new file mode 100644 index 0000000..07c73f0 --- /dev/null +++ b/cocos2d/Grabber.m @@ -0,0 +1,74 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 On-Core + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "Grabber.h" +#import "ccMacros.h" + +#import "Support/Texture2D.h" +#import "Support/OpenGL_Internal.h" + +@implementation Grabber + +-(id) init +{ + if(( self = [super init] )) { + // generate FBO + glGenFramebuffersOES(1, &fbo); + } + return self; +} +-(void)grab:(Texture2D*)texture +{ + glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &oldFBO); + + // bind + glBindFramebufferOES(GL_FRAMEBUFFER_OES, fbo); + + // associate texture with FBO + glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, texture.name, 0); + + // check if it worked (probably worth doing :) ) + GLuint status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES); + if (status != GL_FRAMEBUFFER_COMPLETE_OES) + { + [NSException raise:@"Frame Grabber" format:@"Could not attach texture to framebuffer"]; + } + + glBindFramebufferOES(GL_FRAMEBUFFER_OES, oldFBO); +} + +-(void)beforeRender:(Texture2D*)texture +{ + glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &oldFBO); + glBindFramebufferOES(GL_FRAMEBUFFER_OES, fbo); + + // BUG XXX: doesn't work with RGB565. + glClearColor(0.0f,0.0f,0.0f,0.0f); + + glClear(GL_COLOR_BUFFER_BIT); +} + +-(void)afterRender:(Texture2D*)texture +{ + glBindFramebufferOES(GL_FRAMEBUFFER_OES, oldFBO); +} + +- (void) dealloc +{ + CCLOG( @"deallocing %@", self); + glDeleteFramebuffersOES(1, &fbo); + [super dealloc]; +} + +@end diff --git a/cocos2d/Grid.h b/cocos2d/Grid.h new file mode 100644 index 0000000..c357b9f --- /dev/null +++ b/cocos2d/Grid.h @@ -0,0 +1,114 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 On-Core + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import + +#import "CocosNode.h" +#import "Camera.h" +#import "ccTypes.h" + +@class Texture2D; +@class Grabber; + +/** Base class for other + */ +@interface GridBase : NSObject +{ + BOOL active; + int reuseGrid; + ccGridSize gridSize; + Texture2D * texture; + CGPoint step; + Grabber * grabber; +} + +/** 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) Texture2D *texture; +/** grabber used */ +@property (nonatomic, retain) Grabber *grabber; + +-(id)initWithSize:(ccGridSize)gridSize; +-(void)beforeDraw; +-(void)afterDraw:(Camera*)camera; +-(void)blit; +-(void)reuse; + +@end + +//////////////////////////////////////////////////////////// + +/** + Grid3D is a 3D grid implementation. Each vertex has 3 dimensions: x,y,z + */ +@interface Grid3D : GridBase +{ + GLvoid *texCoordinates; + GLvoid *vertices; + GLvoid *originalVertices; + GLushort *indices; +} + +/** creates a Grid3D (non-tiled) grid with a grid size */ ++(id)gridWithSize:(ccGridSize)gridSize; +/** initizlies a Grid3D (non-tiled) grid with a grid size */ +-(id)initWithSize:(ccGridSize)gridSize; + +/** 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; + +-(void)calculateVertexPoints; + +@end + +//////////////////////////////////////////////////////////// + +/** + TiledGrid3D is a 3D grid implementation. It differs from Grid3D in that + the tiles can be separated from the grid. +*/ +@interface TiledGrid3D : GridBase +{ + GLvoid *texCoordinates; + GLvoid *vertices; + GLvoid *originalVertices; + GLushort *indices; +} + +/** creates a TiledGrid3D with a grid size */ ++(id)gridWithSize:(ccGridSize)gridSize; +/** initializes a TiledGrid3D with a grid size */ +-(id)initWithSize:(ccGridSize)gridSize; + +/** 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; + +-(void)calculateVertexPoints; + +@end diff --git a/cocos2d/Grid.m b/cocos2d/Grid.m new file mode 100644 index 0000000..78e316d --- /dev/null +++ b/cocos2d/Grid.m @@ -0,0 +1,480 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 On-Core + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "ccMacros.h" +#import "Grid.h" +#import "Texture2D.h" +#import "Director.h" +#import "Grabber.h" + +#import "Support/glu.h" +#import "Support/CGPointExtension.h" + +@implementation GridBase + +@synthesize active; +@synthesize reuseGrid; +@synthesize texture; +@synthesize grabber; +@synthesize gridSize; +@synthesize step; + +#define kTextureSize 512 +-(id)initWithSize:(ccGridSize)gSize +{ + if ( (self = [super init] ) ) + { + active = NO; + reuseGrid = 0; + gridSize = gSize; + + CGSize win = [[Director sharedDirector] winSize]; + + if ( self.texture == nil ) + { + Texture2DPixelFormat format = [Director sharedDirector].pixelFormat == kRGB565 ? kTexture2DPixelFormat_RGB565 : kTexture2DPixelFormat_RGBA8888; + + void *data = malloc((int)(kTextureSize * kTextureSize * 4)); + memset(data, 0, (int)(kTextureSize * kTextureSize * 4)); + + texture = [[Texture2D alloc] initWithData:data pixelFormat:format pixelsWide:kTextureSize pixelsHigh:kTextureSize contentSize:win]; + free( data ); + } + + grabber = [[Grabber alloc] init]; + [grabber grab:self.texture]; + + step.x = win.width / gridSize.x; + step.y = win.height / gridSize.y; + } + + return self; +} +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | Dimensions = %ix%i>", [self class], self, gridSize.x, gridSize.y]; +} + +- (void) dealloc +{ + CCLOG( @"deallocing %@", self); + + [texture release]; + [grabber release]; + [super dealloc]; +} + + +// This routine can be merged with Director +-(void)applyLandscape +{ + ccDeviceOrientation orientation = [[Director sharedDirector] deviceOrientation]; + + switch (orientation) { + case CCDeviceOrientationLandscapeLeft: + glTranslatef(160,240,0); + glRotatef(-90,0,0,1); + glTranslatef(-240,-160,0); + break; + case CCDeviceOrientationLandscapeRight: + glTranslatef(160,240,0); + glRotatef(90,0,0,1); + glTranslatef(-240,-160,0); + break; + case CCDeviceOrientationPortraitUpsideDown: + glTranslatef(160,240,0); + glRotatef(180,0,0,1); + glTranslatef(-160,-240,0); + break; + default: + break; + } +} + +-(void)set2DProjection +{ + CGSize winSize = [[Director sharedDirector] winSize]; + + glLoadIdentity(); + glViewport(0, 0, winSize.width, winSize.height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrthof(0, winSize.width, 0, winSize.height, -100, 100); + glMatrixMode(GL_MODELVIEW); +} + +// This routine can be merged with Director +-(void)set3DProjection +{ + CGSize winSize = [[Director sharedDirector] displaySize]; + + 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, [Camera getZEye], + winSize.width/2, winSize.height/2, 0, + 0.0f, 1.0f, 0.0f + ); +} + +-(void)beforeDraw +{ + [self set2DProjection]; + [grabber beforeRender:self.texture]; +} + +-(void)afterDraw:(Camera *)camera +{ + [grabber afterRender:self.texture]; + + [self set3DProjection]; + [self applyLandscape]; + + BOOL cDirty = camera.dirty; + camera.dirty = YES; + [camera locate]; + camera.dirty = cDirty; + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, self.texture.name); + + [self blit]; + + glDisable(GL_TEXTURE_2D); +} + +-(void)blit +{ + [NSException raise:@"GridBase" format:@"Abstract class needs implementation"]; +} + +-(void)reuse +{ + [NSException raise:@"GridBase" format:@"Abstract class needs implementation"]; +} + +@end + +//////////////////////////////////////////////////////////// + +@implementation Grid3D + ++(id)gridWithSize:(ccGridSize)gridSize +{ + return [[[Grid3D alloc] initWithSize:gridSize] autorelease]; +} + +-(id)initWithSize:(ccGridSize)gSize +{ + if ( (self = [super initWithSize:gSize] ) ) + { + [self calculateVertexPoints]; + } + + return self; +} + +-(void)dealloc +{ + free(texCoordinates); + free(vertices); + free(indices); + free(originalVertices); + [super dealloc]; +} + +-(void)blit +{ + int n = gridSize.x * gridSize.y; + + glEnableClientState( GL_VERTEX_ARRAY); + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + + glVertexPointer(3, GL_FLOAT, 0, vertices); + glTexCoordPointer(2, GL_FLOAT, 0, texCoordinates); + glDrawElements(GL_TRIANGLES, n*6, GL_UNSIGNED_SHORT, indices); + + glDisableClientState(GL_VERTEX_ARRAY ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); +} + +-(void)calculateVertexPoints +{ + float width = (float)self.texture.pixelsWide; + float height = (float)self.texture.pixelsHigh; + + 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( y = 0; y < (gridSize.y+1); y++ ) + { + for( x = 0; x < (gridSize.x+1); x++ ) + { + int idx = (y * (gridSize.x+1)) + x; + + vertArray[idx*3] = -1; + vertArray[idx*3+1] = -1; + vertArray[idx*3+2] = -1; + texArray[idx*2] = -1; + texArray[idx*2+1] = -1; + } + } + + for( x = 0; x < gridSize.x; x++ ) + { + for( y = 0; y < gridSize.y; y++ ) + { + int 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; + texArray[ tex1[i] + 1 ] = tex2[i].y / height; + } + } + } + + memcpy(originalVertices, vertices, (gridSize.x+1)*(gridSize.y+1)*sizeof(ccVertex3F)); +} + +-(ccVertex3F)vertex:(ccGridSize)pos +{ + int 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 +{ + int 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 +{ + int 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 + +//////////////////////////////////////////////////////////// + +@implementation TiledGrid3D + ++(id)gridWithSize:(ccGridSize)gridSize +{ + return [[[TiledGrid3D alloc] initWithSize:gridSize] autorelease]; +} + +-(id)initWithSize:(ccGridSize)gSize +{ + if ( (self = [super initWithSize:gSize] ) ) + { + [self calculateVertexPoints]; + } + + return self; +} + +-(void)dealloc +{ + free(texCoordinates); + free(vertices); + free(indices); + free(originalVertices); + [super dealloc]; +} + +-(void)blit +{ + int n = gridSize.x * gridSize.y; + + glEnableClientState( GL_VERTEX_ARRAY); + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + + glVertexPointer(3, GL_FLOAT, 0, vertices); + glTexCoordPointer(2, GL_FLOAT, 0, texCoordinates); + glDrawElements(GL_TRIANGLES, n*6, GL_UNSIGNED_SHORT, indices); + + glDisableClientState(GL_VERTEX_ARRAY ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); +} + +-(void)calculateVertexPoints +{ + float width = (float)self.texture.pixelsWide; + float height = (float)self.texture.pixelsHigh; + + int 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; + + *texArray++ = x1 / width; + *texArray++ = y1 / height; + *texArray++ = x2 / width; + *texArray++ = y1 / height; + *texArray++ = x1 / width; + *texArray++ = y2 / height; + *texArray++ = x2 / width; + *texArray++ = y2 / 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 +{ + int idx = (gridSize.y * pos.x + pos.y) * 4 * 3; + float *vertArray = (float*)vertices; + memcpy(&vertArray[idx], &coords, sizeof(ccQuad3)); +} + +-(ccQuad3)originalTile:(ccGridSize)pos +{ + int 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 +{ + int 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 ) + { + int numQuads = gridSize.x * gridSize.y; + + memcpy(originalVertices, vertices, numQuads*12*sizeof(GLfloat)); + reuseGrid--; + } +} + +@end diff --git a/cocos2d/Grid3DAction.h b/cocos2d/Grid3DAction.h new file mode 100644 index 0000000..f307ed1 --- /dev/null +++ b/cocos2d/Grid3DAction.h @@ -0,0 +1,193 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 On-Core + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "GridAction.h" + +/** Waves3D action */ +@interface Waves3D : Grid3DAction +{ + 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 + +//////////////////////////////////////////////////////////// + +/** FlipX3D action */ +@interface FlipX3D : Grid3DAction +{ +} + +/** creates the action with duration */ ++(id) actionWithDuration:(ccTime)d; +/** initizlies the action with duration */ +-(id) initWithDuration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** FlipY3D action */ +@interface FlipY3D : FlipX3D +{ +} + +@end + +//////////////////////////////////////////////////////////// + +/** Lens3D action */ +@interface Lens3D : Grid3DAction +{ + CGPoint position; + float radius; + float lensEffect; + CGPoint lastPosition; +} + +/** lens effect. Defaults to 0.7 - 0 means no effect, 1 is very strong effect */ +@property (nonatomic,readwrite) float lensEffect; +/** lens center position */ +@property (nonatomic,readwrite) CGPoint position; + +/** creates the action with center position, 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, radius, a grid size and duration */ +-(id)initWithPosition:(CGPoint)pos radius:(float)r grid:(ccGridSize)gridSize duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** Ripple3D action */ +@interface Ripple3D : Grid3DAction +{ + CGPoint position; + float radius; + int waves; + float amplitude; + float amplitudeRate; +} + +/** center position */ +@property (nonatomic,readwrite) CGPoint position; +/** amplitude */ +@property (nonatomic,readwrite) float amplitude; +/** amplitude rate */ +@property (nonatomic,readwrite) float amplitudeRate; + +/** creates the action with 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 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 + +//////////////////////////////////////////////////////////// + +/** Shaky3D action */ +@interface Shaky3D : Grid3DAction +{ + 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 + +//////////////////////////////////////////////////////////// + +/** Liquid action */ +@interface Liquid : Grid3DAction +{ + 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 + +//////////////////////////////////////////////////////////// + +/** Waves action */ +@interface Waves : Grid3DAction +{ + 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 + +//////////////////////////////////////////////////////////// + +/** Twirl action */ +@interface Twirl : Grid3DAction +{ + CGPoint position; + 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/cocos2d/Grid3DAction.m b/cocos2d/Grid3DAction.m new file mode 100644 index 0000000..ad06355 --- /dev/null +++ b/cocos2d/Grid3DAction.m @@ -0,0 +1,522 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 On-Core + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "Grid3DAction.h" +#import "Support/CGPointExtension.h" + +@implementation Waves3D + +@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 = 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 + +//////////////////////////////////////////////////////////// + +@implementation FlipX3D + ++(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]; +} + +-(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 + +//////////////////////////////////////////////////////////// + +@implementation FlipY3D + +-(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 + +//////////////////////////////////////////////////////////// + +@implementation Lens3D + +@synthesize lensEffect; +@synthesize position; + ++(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]) ) + { + self.position = pos; + radius = r; + lensEffect = 0.7f; + lastPosition = ccp(-1,-1); + } + + return self; +} + +-(void)update:(ccTime)time +{ + if ( position.x != lastPosition.x || position.y != lastPosition.y ) + { + 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(position, 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]; + } + } + + lastPosition = position; + } +} + +@end + +//////////////////////////////////////////////////////////// + +@implementation Ripple3D + +@synthesize position; +@synthesize amplitude; +@synthesize 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; +} + +-(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(position, 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 + +//////////////////////////////////////////////////////////// + +@implementation Shaky3D + ++(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; +} + +-(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 + +//////////////////////////////////////////////////////////// + +@implementation Liquid + +@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]; + } + } +} + +@end + +//////////////////////////////////////////////////////////// + +@implementation Waves + +@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]; + } + } +} + +@end + +//////////////////////////////////////////////////////////// + +@implementation Twirl + +@synthesize position; +@synthesize amplitude; +@synthesize 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)update:(ccTime)time +{ + int i, j; + CGPoint c = position; + + 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; + + CGPoint d; + + d.x = sinf(a) * (v.y-c.y) + cosf(a) * (v.x-c.x); + d.y = cosf(a) * (v.y-c.y) - sinf(a) * (v.x-c.x); + + v.x = c.x + d.x; + v.y = c.y + d.y; + + [self setVertex:ccg(i,j) vertex:v]; + } + } +} + +@end diff --git a/cocos2d/GridAction.h b/cocos2d/GridAction.h new file mode 100644 index 0000000..cdccf53 --- /dev/null +++ b/cocos2d/GridAction.h @@ -0,0 +1,157 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 On-Core + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import + +#import "IntervalAction.h" +#import "InstantAction.h" +#import "Grid.h" + +@class GridBase; + +/** Base class for Grid actions */ +@interface GridAction : IntervalAction +{ + 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 */ +-(GridBase *)grid; + +@end + +//////////////////////////////////////////////////////////// + +/** Base class for Grid3D actions. + Grid3D actions can modify a non-tiled grid. + */ +@interface Grid3DAction : GridAction +{ + +} + +/** 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 TiledGrid3D actions */ +@interface TiledGrid3DAction : GridAction +{ + +} + +/** 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 + +//////////////////////////////////////////////////////////// + +/** AccelDeccelAmplitude action */ +@interface AccelDeccelAmplitude : IntervalAction +{ + float rate; + IntervalAction *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:(Action*)action duration:(ccTime)d; +/** initializes the action with an inner action that has the amplitude property, and a duration time */ +-(id)initWithAction:(Action*)action duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** AccelAmplitude action */ +@interface AccelAmplitude : IntervalAction +{ + float rate; + IntervalAction *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:(Action*)action duration:(ccTime)d; +/** initializes the action with an inner action that has the amplitude property, and a duration time */ +-(id)initWithAction:(Action*)action duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** DeccelAmplitude action */ +@interface DeccelAmplitude : IntervalAction +{ + float rate; + IntervalAction *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:(Action*)action duration:(ccTime)d; +/** initializes the action with an inner action that has the amplitude property, and a duration time */ +-(id)initWithAction:(Action*)action duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** StopGrid 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 StopGrid : InstantAction +{ +} +@end + +//////////////////////////////////////////////////////////// + +/** ReuseGrid action */ +@interface ReuseGrid : InstantAction +{ + 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/cocos2d/GridAction.m b/cocos2d/GridAction.m new file mode 100644 index 0000000..1f02d01 --- /dev/null +++ b/cocos2d/GridAction.m @@ -0,0 +1,348 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 On-Core + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "GridAction.h" +#import "Director.h" + +@implementation GridAction + +@synthesize 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)start +{ + [super start]; + + GridBase *newgrid = [self grid]; + + CocosNode *t = (CocosNode*) target; + + if ( t.grid && t.grid.reuseGrid > 0 ) + { + if ( t.grid.active && t.grid.gridSize.x == gridSize.x && t.grid.gridSize.y == gridSize.y && [t.grid isKindOfClass:[newgrid class]] ) + { + [t.grid reuse]; + } + else + { + [NSException raise:@"GridBase" format:@"Cannot reuse grid"]; + } + } + else + { + if ( t.grid && t.grid.active ) + t.grid.active = NO; + t.grid = newgrid; + t.grid.active = YES; + } +} + +-(GridBase *)grid +{ + [NSException raise:@"GridBase" format:@"Abstract class needs implementation"]; + return nil; +} + +- (IntervalAction*) reverse +{ + return [ReverseTime actionWithAction:self]; +} +@end + +//////////////////////////////////////////////////////////// + +@implementation Grid3DAction + +-(GridBase *)grid +{ + return [Grid3D gridWithSize:gridSize]; +} + +-(ccVertex3F)vertex:(ccGridSize)pos +{ + Grid3D *g = (Grid3D *)[target grid]; + return [g vertex:pos]; +} + +-(ccVertex3F)originalVertex:(ccGridSize)pos +{ + Grid3D *g = (Grid3D *)[target grid]; + return [g originalVertex:pos]; +} + +-(void)setVertex:(ccGridSize)pos vertex:(ccVertex3F)vertex +{ + Grid3D *g = (Grid3D *)[target grid]; + return [g setVertex:pos vertex:vertex]; +} +@end + +//////////////////////////////////////////////////////////// + +@implementation TiledGrid3DAction + +-(GridBase *)grid +{ + return [TiledGrid3D gridWithSize:gridSize]; +} + +-(ccQuad3)tile:(ccGridSize)pos +{ + TiledGrid3D *g = (TiledGrid3D *)[target grid]; + return [g tile:pos]; +} + +-(ccQuad3)originalTile:(ccGridSize)pos +{ + TiledGrid3D *g = (TiledGrid3D *)[target grid]; + return [g originalTile:pos]; +} + +-(void)setTile:(ccGridSize)pos coords:(ccQuad3)coords +{ + TiledGrid3D *g = (TiledGrid3D *)[target grid]; + [g setTile:pos coords:coords]; +} + +@end + +//////////////////////////////////////////////////////////// + +@interface IntervalAction (Amplitude) +-(void)setAmplitudeRate:(CGFloat)amp; +-(CGFloat)getAmplitudeRate; +@end + +@implementation IntervalAction (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 + +//////////////////////////////////////////////////////////// + +@implementation AccelDeccelAmplitude + +@synthesize rate; + ++(id)actionWithAction:(Action*)action duration:(ccTime)d +{ + return [[[self alloc] initWithAction:action duration:d ] autorelease]; +} + +-(id)initWithAction:(Action *)action duration:(ccTime)d +{ + if ( (self = [super initWithDuration:d]) ) + { + rate = 1.0f; + other = [action retain]; + } + + return self; +} + +-(void)dealloc +{ + [other release]; + [super dealloc]; +} + +-(void)start +{ + [super start]; + other.target = self.target; + [other start]; +} + +-(void) update: (ccTime) time; +{ + float f = time*2; + + if (f > 1) + { + f -= 1; + f = 1 - f; + } + + [other setAmplitudeRate:powf(f, rate)]; + [other update:time]; +} + +- (IntervalAction*) reverse +{ + return [AccelDeccelAmplitude actionWithAction:[other reverse] duration:self.duration]; +} + +@end + +//////////////////////////////////////////////////////////// + +@implementation AccelAmplitude + +@synthesize rate; + ++(id)actionWithAction:(Action*)action duration:(ccTime)d +{ + return [[[self alloc] initWithAction:action duration:d ] autorelease]; +} + +-(id)initWithAction:(Action *)action duration:(ccTime)d +{ + if ( (self = [super initWithDuration:d]) ) + { + rate = 1.0f; + other = [action retain]; + } + + return self; +} + +-(void)dealloc +{ + [other release]; + [super dealloc]; +} + +-(void)start +{ + [super start]; + other.target = self.target; + [other start]; +} + +-(void) update: (ccTime) time; +{ + [other setAmplitudeRate:powf(time, rate)]; + [other update:time]; +} + +- (IntervalAction*) reverse +{ + return [AccelAmplitude actionWithAction:[other reverse] duration:self.duration]; +} + +@end + +//////////////////////////////////////////////////////////// + +@implementation DeccelAmplitude + +@synthesize rate; + ++(id)actionWithAction:(Action*)action duration:(ccTime)d +{ + return [[[self alloc] initWithAction:action duration:d ] autorelease]; +} + +-(id)initWithAction:(Action *)action duration:(ccTime)d +{ + if ( (self = [super initWithDuration:d]) ) + { + rate = 1.0f; + other = [action retain]; + } + + return self; +} + +-(void)dealloc +{ + [other release]; + [super dealloc]; +} + +-(void)start +{ + [super start]; + other.target = self.target; + [other start]; +} + +-(void) update: (ccTime) time; +{ + [other setAmplitudeRate:powf((1-time), rate)]; + [other update:time]; +} + +- (IntervalAction*) reverse +{ + return [DeccelAmplitude actionWithAction:[other reverse] duration:self.duration]; +} + +@end + +//////////////////////////////////////////////////////////// + +@implementation StopGrid + +-(void)start +{ + [super start]; + + if ( [[self target] grid] && [[[self target] grid] active] ) + [[[self target] grid] setActive: NO]; +} + +@end + +//////////////////////////////////////////////////////////// + +@implementation ReuseGrid + ++(id)actionWithTimes:(int)times +{ + return [[[self alloc] initWithTimes:times ] autorelease]; +} + +-(id)initWithTimes:(int)times +{ + if ( (self = [super init]) ) + { + t = times; + } + + return self; +} + +-(void)start +{ + [super start]; + + CocosNode *myTarget = (CocosNode*) [self target]; + if ( myTarget.grid && myTarget.grid.active ) + myTarget.grid.reuseGrid += t; +} + +@end diff --git a/cocos2d/InstantAction.h b/cocos2d/InstantAction.h new file mode 100644 index 0000000..95eb544 --- /dev/null +++ b/cocos2d/InstantAction.h @@ -0,0 +1,99 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import + +#import "Action.h" + +/** Instant actions are immediate actions. They don't have a duration like + the Interval Actions. +*/ +@interface InstantAction : FiniteTimeAction +{} +@end + +/** Show the node + */ + @interface Show : InstantAction +{ +} +@end + +/** Hide the node + */ +@interface Hide : InstantAction +{ +} +@end + +/** Toggles the visibility of a node + */ +@interface ToggleVisibility : InstantAction +{ +} +@end + +/** Places the node in a certain position + */ +@interface Place : InstantAction +{ + 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 CallFunc : InstantAction +{ + id targetCallback; + SEL selector; +} +/** 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 CallFuncN : CallFunc +{ +} +@end + +/** Calls a 'callback' with the node as the first argument and the 2nd argument is data + * ND means: Node Data + */ +@interface CallFuncND : CallFuncN +{ + void *data; + NSInvocation *invocation_; +} + +/** Invocation object that has the target#selector and the parameters */ +@property (nonatomic,readwrite,retain) NSInvocation *invocation; + +/** 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 diff --git a/cocos2d/InstantAction.m b/cocos2d/InstantAction.m new file mode 100644 index 0000000..dcdaebc --- /dev/null +++ b/cocos2d/InstantAction.m @@ -0,0 +1,223 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import "InstantAction.h" +#import "CocosNode.h" + +// +// InstantAction +// +@implementation InstantAction + +-(id) init +{ + if( (self=[super init]) ) + duration = 0; + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + InstantAction *copy = [[[self class] allocWithZone: zone] init]; + return copy; +} + +- (BOOL) isDone +{ + return YES; +} +-(void) step: (ccTime) dt +{ + [self update: 1]; +} +-(void) update: (ccTime) t +{ + // ignore +} +-(FiniteTimeAction*) reverse +{ + return [[self copy] autorelease]; +} +@end + +// +// Show +// +@implementation Show +-(void) start +{ + [super start]; + [target setVisible: YES]; +} +-(FiniteTimeAction*) reverse +{ + return [Hide action]; +} +@end + +// +// Hide +// +@implementation Hide +-(void) start +{ + [super start]; + [target setVisible: NO]; +} +-(FiniteTimeAction*) reverse +{ + return [Show action]; +} +@end + +// +// ToggleVisibility +// +@implementation ToggleVisibility +-(void) start +{ + [super start]; + BOOL v = [target visible]; + [target setVisible: !v]; +} +@end + +// +// Place +// +@implementation Place ++(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 +{ + InstantAction *copy = [[[self class] allocWithZone: zone] initWithPosition: position]; + return copy; +} + +-(void) start +{ + [super start]; + [target setPosition: position]; +} +@end + +// +// CallFunc +// +@implementation CallFunc ++(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]) ) { + targetCallback = [t retain]; + selector = s; + } + return self; +} + +-(void) dealloc +{ + [targetCallback release]; + [super dealloc]; +} + +-(id) copyWithZone: (NSZone*) zone +{ + InstantAction *copy = [[[self class] allocWithZone: zone] initWithTarget:targetCallback selector:selector]; + return copy; +} + + +-(void) start +{ + [super start]; + [self execute]; +} + +-(void) execute +{ + [targetCallback performSelector:selector]; +} +@end + +// +// CallFuncN +// +@implementation CallFuncN + +-(void) execute +{ + [targetCallback performSelector:selector withObject:target]; +} +@end + +// +// CallFuncND +// +@implementation CallFuncND + +@synthesize invocation = invocation_; + ++(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; + NSMethodSignature * sig = [[t class] instanceMethodSignatureForSelector:s]; + self.invocation = [NSInvocation invocationWithMethodSignature:sig]; + [invocation_ setTarget:t]; + [invocation_ setSelector:s]; + } + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + InstantAction *copy = [[[self class] allocWithZone: zone] initWithTarget:targetCallback selector:selector data:data]; + return copy; +} + + +-(void) dealloc +{ + [invocation_ release]; + [super dealloc]; +} + +-(void) execute +{ + [invocation_ setArgument:&target atIndex:2]; + [invocation_ setArgument:&data atIndex:3]; + [invocation_ invoke]; +} +@end diff --git a/cocos2d/IntervalAction.h b/cocos2d/IntervalAction.h new file mode 100644 index 0000000..4423931 --- /dev/null +++ b/cocos2d/IntervalAction.h @@ -0,0 +1,356 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "CocosNode.h" +#import "Action.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 IntervalAction 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: + + Action * pingPongAction = [Sequence actions: action, [action reverse], nil]; +*/ +@interface IntervalAction: FiniteTimeAction +{ + ccTime elapsed; + BOOL firstTick; +} + +@property (nonatomic,readonly) ccTime elapsed; + +/** creates the action */ ++(id) actionWithDuration: (ccTime) d; +/** initializes the action */ +-(id) initWithDuration: (ccTime) d; +/** called when the action is about to start */ +-(void) start; +/** returns YES if the action has finished */ +-(BOOL) isDone; +/** returns a reversed action */ +- (IntervalAction*) reverse; +@end + +/** Runs actions sequentially, one after another + */ +@interface Sequence : IntervalAction +{ + NSArray *actions; + ccTime split; + int last; +} +/** helper contructor to create an array of sequenceable actions */ ++(id) actions: (FiniteTimeAction*) action1, ... NS_REQUIRES_NIL_TERMINATION; +/** creates the action */ ++(id) actionOne:(FiniteTimeAction*)actionOne two:(FiniteTimeAction*)actionTwo; +/** initializes the action */ +-(id) initOne:(FiniteTimeAction*)actionOne two:(FiniteTimeAction*)actionTwo; +@end + + +/** Repeats an action a number of times. + * To repeat an action forever use the RepeatForever action. + */ +@interface Repeat : IntervalAction +{ + unsigned int times; + unsigned int total; + FiniteTimeAction *other; +} +/** creates the Repeat action. Times is an unsigned integer between 1 and pow(2,30) */ ++(id) actionWithAction:(FiniteTimeAction*)action times: (unsigned int)times; +/** initializes the action. Times is an unsigned integer between 1 and pow(2,30) */ +-(id) initWithAction:(FiniteTimeAction*)action times: (unsigned int)times; +@end + +/** Spawn a new action immediately + */ +@interface Spawn : IntervalAction +{ + FiniteTimeAction *one; + FiniteTimeAction *two; +} +/** helper constructor to create an array of spawned actions */ ++(id) actions: (FiniteTimeAction*) action1, ... NS_REQUIRES_NIL_TERMINATION; +/** creates the Spawn action */ ++(id) actionOne: (FiniteTimeAction*) one two:(FiniteTimeAction*) two; +/** initializes the Spawn action with the 2 actions to spawn */ +-(id) initOne: (FiniteTimeAction*) one two:(FiniteTimeAction*) two; +@end + +/** Rotates a CocosNode object to a certain angle by modifying it's + rotation attribute. + The direction will be decided by the shortest angle. +*/ +@interface RotateTo : IntervalAction +{ + float angle; + float startAngle; +} +/** creates the action */ ++(id) actionWithDuration:(ccTime)duration angle:(float)angle; +/** initializes the action */ +-(id) initWithDuration:(ccTime)duration angle:(float)angle; +@end + +/** Rotates a CocosNode object clockwise a number of degrees by modiying it's rotation attribute. +*/ +@interface RotateBy : IntervalAction +{ + 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 CocosNode object to the position x,y. x and y are absolute coordinates by modifying it's position attribute. +*/ +@interface MoveTo : IntervalAction +{ + 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 CocosNode 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 MoveBy : MoveTo +{ +} +/** creates the action */ ++(id) actionWithDuration: (ccTime)duration position:(CGPoint)deltaPosition; +/** initializes the action */ +-(id) initWithDuration: (ccTime)duration position:(CGPoint)deltaPosition; +@end + +/** Moves a CocosNode object simulating a jump movement by modifying it's position attribute. +*/ + @interface JumpBy : IntervalAction +{ + 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 CocosNode object to a position simulating a jump movement by modifying it's position attribute. +*/ + @interface JumpTo : JumpBy +{ +} +@end + +/** bezier configuration structure + */ +typedef struct _ccBezierConfig { + //! startPosition of the bezier + CGPoint startPosition; + //! end position of the bezier + CGPoint endPosition; + //! Bezier control point 1 + CGPoint controlPoint_1; + //! Bezier control point 2 + CGPoint controlPoint_2; +} ccBezierConfig; + +/** A action that moves the target with a cubic Bezier curve. + Since BezierBy moves the target "relative" it will be easier if + the startPosition of the Bezier configuration is (0,0) + */ +@interface BezierBy : IntervalAction +{ + 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 + +/** Scales a CocosNode object to a zoom factor by modifying it's scale attribute. + @warning This action doesn't support "reverse" + */ +@interface ScaleTo : IntervalAction +{ + 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 CocosNode object a zoom factor by modifying it's scale attribute. +*/ +@interface ScaleBy : ScaleTo +{ +} +@end + +/** Blinks a CocosNode object by modifying it's visible attribute +*/ +@interface Blink : IntervalAction +{ + int times; +} +/** creates the action */ ++(id) actionWithDuration: (ccTime)duration blinks:(unsigned int)blinks; +/** initilizes the action */ +-(id) initWithDuration: (ccTime)duration blinks:(unsigned int)blinks; +@end + +/** Fades In an object that implements the CocosNodeRGBA protocol. It modifies the opacity from 0 to 255. + The "reverse" of this action is FadeOut + */ +@interface FadeIn : IntervalAction +{ +} +@end + +/** Fades Out an object that implements the CocosNodeRGBA protocol. It modifies the opacity from 255 to 0. + The "reverse" of this action is FadeIn +*/ +@interface FadeOut : IntervalAction +{ +} +@end + +/** Fades an object that implements the CocosNodeRGBA protocol. It modifies the opacity from the current value to a custom one. + @warning This action doesn't support "reverse" + */ +@interface FadeTo : IntervalAction +{ + 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 CocosNode that implements the CocosNodeRGB protocol from current tint to a custom one. + @warning This action doesn't support "reverse" + @since v0.7.2 +*/ +@interface TintTo : IntervalAction +{ + 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 CocosNode that implements the CocosNodeRGB protocol from current tint to a custom one. + @since v0.7.2 + */ +@interface TintBy : IntervalAction +{ + 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 DelayTime : IntervalAction +{ +} +@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 ReverseTime : IntervalAction +{ + FiniteTimeAction * other; +} +/** creates the action */ ++(id) actionWithAction: (FiniteTimeAction*) action; +/** initializes the action */ +-(id) initWithAction: (FiniteTimeAction*) action; +@end + + +@class Animation; +@class Texture2D; +/** Animates a sprite given the name of an Animation */ +@interface Animate : IntervalAction +{ + Animation *animation; + id origFrame; + BOOL restoreOriginalFrame; +} +/** creates the action with an Animation and will restore the original frame when the animation is over */ ++(id) actionWithAnimation:(id) a; +/** initializes the action with an Animation and will restore the original frame when the animtion is over */ +-(id) initWithAnimation:(id) a; +/** creates the action with an Animation */ ++(id) actionWithAnimation:(id) a restoreOriginalFrame:(BOOL)b; +/** initializes the action with an Animation */ +-(id) initWithAnimation:(id) a restoreOriginalFrame:(BOOL)b; +@end + + diff --git a/cocos2d/IntervalAction.m b/cocos2d/IntervalAction.m new file mode 100644 index 0000000..02e2441 --- /dev/null +++ b/cocos2d/IntervalAction.m @@ -0,0 +1,1131 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import "IntervalAction.h" +#import "Sprite.h" +#import "CocosNode.h" +#import "Support/CGPointExtension.h" + +// +// IntervalAction +// +#pragma mark - +#pragma mark IntervalAction +@implementation IntervalAction + +@synthesize elapsed; + +-(id) init +{ + NSException* myException = [NSException + exceptionWithName:@"IntervalActionInit" + reason:@"Init not supported. Use InitWithDuration" + userInfo:nil]; + @throw myException; + +} + ++(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 = 0.00000001f; + elapsed = 0; + firstTick = YES; + } + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + Action *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) start +{ + elapsed = 0.0f; + firstTick = YES; +} + +- (IntervalAction*) reverse +{ + NSException* myException = [NSException + exceptionWithName:@"ReverseActionNotImplemented" + reason:@"Reverse Action not implemented" + userInfo:nil]; + @throw myException; +} +@end + +// +// Sequence +// +#pragma mark - +#pragma mark Sequence +@implementation Sequence ++(id) actionOne: (FiniteTimeAction*) one two: (FiniteTimeAction*) two +{ + return [[[self alloc] initOne:one two:two ] autorelease]; +} + ++(id) actions: (FiniteTimeAction*) action1, ... +{ + va_list params; + va_start(params,action1); + + FiniteTimeAction *now; + FiniteTimeAction *prev = action1; + + while( action1 ) { + now = va_arg(params,FiniteTimeAction*); + if ( now ) + prev = [Sequence actionOne: prev two: now]; + else + break; + } + va_end(params); + return prev; +} + +-(id) initOne: (FiniteTimeAction*) one_ two: (FiniteTimeAction*) two_ +{ + NSAssert( one_!=nil, @"Sequence: argument one must be non-nil"); + NSAssert( two_!=nil, @"Sequence: argument two must be non-nil"); + + FiniteTimeAction *one = one_; + FiniteTimeAction *two = two_; + + ccTime d = [one duration] + [two duration]; + [super initWithDuration: d]; + + actions = [[NSArray arrayWithObjects: one, two, nil] retain]; + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + Action *copy = [[[self class] allocWithZone:zone] initOne:[[[actions objectAtIndex:0] copy] autorelease] two:[[[actions objectAtIndex:1] copy] autorelease] ]; + return copy; +} + +-(void) dealloc +{ + [actions release]; + [super dealloc]; +} + +-(void) start +{ + [super start]; + for( Action * action in actions ) + [action setTarget: target]; + + split = [[actions objectAtIndex:0] duration] / duration; + last = -1; +} + +-(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 objectAtIndex:0] start]; + [[actions objectAtIndex:0] update:1.0f]; + [[actions objectAtIndex:0] stop]; + } + + if (last != found ) { + if( last != -1 ) { + [[actions objectAtIndex: last] update: 1.0f]; + [[actions objectAtIndex: last] stop]; + } + [[actions objectAtIndex: found] start]; + } + [[actions objectAtIndex:found] update: new_t]; + last = found; +} + +- (IntervalAction *) reverse +{ + return [Sequence actionOne: [[actions objectAtIndex:1] reverse] two: [[actions objectAtIndex:0] reverse ] ]; +} +@end + +// +// Repeat +// +#pragma mark - +#pragma mark Repeat +@implementation Repeat ++(id) actionWithAction: (FiniteTimeAction*) action times: (unsigned int) t +{ + return [[[self alloc] initWithAction: action times: t] autorelease]; +} + +-(id) initWithAction: (FiniteTimeAction*) action times: (unsigned int) t +{ + ccTime d = [action duration] * t; + + if( (self=[super initWithDuration: d ]) ) { + times = t; + other = [action retain]; + + total = 0; + } + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + Action *copy = [[[self class] allocWithZone:zone] initWithAction:[[other copy] autorelease] times:times]; + return copy; +} + +-(void) dealloc +{ + [other release]; + [super dealloc]; +} + +-(void) start +{ + total = 0; + [super start]; + [other setTarget: target]; + [other start]; +} + +//-(void) step:(ccTime) dt +//{ +// [other step: dt]; +// if( [other isDone] ) { +// total++; +// [other start]; +// } +//} + +// 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; + float r = fmodf(t, 1.0f); + if( t > total+1 ) { + [other update:1.0f]; + total++; + [other stop]; + [other start]; + [other update:0.0f]; + } else { + // fix last repeat position + // else it could be 0. + if( dt== 1.0f) { + r=1.0f; + total++; // this is the added line + } + [other update: MIN(r,1)]; + } +} + +-(BOOL) isDone +{ + return ( total == times ); +} + +- (IntervalAction *) reverse +{ + return [Repeat actionWithAction:[other reverse] times: times]; +} +@end + +// +// Spawn +// +#pragma mark - +#pragma mark Spawn + +@implementation Spawn ++(id) actions: (FiniteTimeAction*) action1, ... +{ + va_list params; + va_start(params,action1); + + FiniteTimeAction *now; + FiniteTimeAction *prev = action1; + + while( action1 ) { + now = va_arg(params,FiniteTimeAction*); + if ( now ) + prev = [Spawn actionOne: prev two: now]; + else + break; + } + va_end(params); + return prev; +} + ++(id) actionOne: (FiniteTimeAction*) one two: (FiniteTimeAction*) two +{ + return [[[self alloc] initOne:one two:two ] autorelease]; +} + +-(id) initOne: (FiniteTimeAction*) one_ two: (FiniteTimeAction*) two_ +{ + NSAssert( one_!=nil, @"Spawn: argument one must be non-nil"); + NSAssert( two_!=nil, @"Spawn: argument two must be non-nil"); + + ccTime d1 = [one_ duration]; + ccTime d2 = [two_ duration]; + + [super initWithDuration: fmaxf(d1,d2)]; + + one = one_; + two = two_; + + if( d1 > d2 ) + two = [Sequence actionOne: two_ two:[DelayTime actionWithDuration: (d1-d2)] ]; + else if( d1 < d2) + one = [Sequence actionOne: one_ two: [DelayTime actionWithDuration: (d2-d1)] ]; + + [one retain]; + [two retain]; + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + Action *copy = [[[self class] allocWithZone: zone] initOne: [[one copy] autorelease] two: [[two copy] autorelease] ]; + return copy; +} + +-(void) dealloc +{ + [one release]; + [two release]; + [super dealloc]; +} + +-(void) start +{ + [super start]; + [one setTarget: target]; + [two setTarget: target]; + [one start]; + [two start]; +} + +-(void) update: (ccTime) t +{ + [one update:t]; + [two update:t]; +} + +- (IntervalAction *) reverse +{ + return [Spawn actionOne: [one reverse] two: [two reverse ] ]; +} +@end + +// +// RotateTo +// +#pragma mark - +#pragma mark RotateTo + +@implementation RotateTo ++(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 +{ + Action *copy = [[[self class] allocWithZone: zone] initWithDuration:[self duration] angle: angle]; + return copy; +} + +-(void) start +{ + [super start]; + + // + if (startAngle > 0) + startAngle = fmodf(startAngle, 360.0f); + else + startAngle = fmodf(startAngle, -360.0f); + + startAngle = [target rotation]; + angle -= startAngle; + if (angle > 180) + angle = -360 + angle; + if (angle < -180) + angle = 360 + angle; +} +-(void) update: (ccTime) t +{ + [target setRotation: startAngle + angle * t]; +} +@end + + +// +// RotateBy +// +#pragma mark - +#pragma mark RotateBy + +@implementation RotateBy ++(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]) ) + return nil; + + angle = a; + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + Action *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] angle: angle]; + return copy; +} + +-(void) start +{ + [super start]; + startAngle = [target rotation]; +} + +-(void) update: (ccTime) t +{ + // XXX: shall I add % 360 + [target setRotation: (startAngle + angle * t )]; +} + +-(IntervalAction*) reverse +{ + return [RotateBy actionWithDuration: duration angle: -angle]; +} + +@end + +// +// MoveTo +// +#pragma mark - +#pragma mark MoveTo + +@implementation MoveTo ++(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]) ) + return nil; + + endPosition = p; + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + Action *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] position: endPosition]; + return copy; +} + +-(void) start +{ + [super start]; + startPosition = [(CocosNode*)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 MoveBy ++(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]) ) + return nil; + + delta = p; + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + Action *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] position: delta]; + return copy; +} + +-(void) start +{ + CGPoint dTmp = delta; + [super start]; + delta = dTmp; +} + +-(IntervalAction*) reverse +{ + return [MoveBy actionWithDuration: duration position: ccp( -delta.x, -delta.y)]; +} +@end + +// +// JumpBy +// +#pragma mark - +#pragma mark JumpBy + +@implementation JumpBy ++(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]) ) + return nil; + + delta = pos; + height = h; + jumps = j; + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + Action *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] position: delta height:height jumps:jumps]; + return copy; +} + +-(void) start +{ + [super start]; + startPosition = [(CocosNode*)target position]; +} + +-(void) update: (ccTime) t +{ + 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 )]; +} + +-(IntervalAction*) reverse +{ + return [JumpBy actionWithDuration: duration position: ccp(-delta.x,-delta.y) height: height jumps:jumps]; +} +@end + +// +// JumpTo +// +#pragma mark - +#pragma mark JumpTo + +@implementation JumpTo +-(void) start +{ + [super start]; + 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 BezierBy ++(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 +{ + Action *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] bezier: config]; + return copy; +} + +-(void) start +{ + [super start]; + startPosition = [(CocosNode*)target position]; +} + +-(void) update: (ccTime) t +{ + float xa = config.startPosition.x; + float xb = config.controlPoint_1.x; + float xc = config.controlPoint_2.x; + float xd = config.endPosition.x; + + float ya = config.startPosition.y; + 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))]; +} + +- (IntervalAction*) reverse +{ + // XXX: reverse it's not working as expected + ccBezierConfig r; + r.startPosition = ccpNeg( config.startPosition); + r.endPosition = ccpNeg(config.endPosition); + r.controlPoint_1 = ccpNeg(config.controlPoint_1); + r.controlPoint_2 = ccpNeg(config.controlPoint_2); + + BezierBy *action = [BezierBy actionWithDuration:[self duration] bezier:r]; + return action; +} +@end + +// +// ScaleTo +// +#pragma mark - +#pragma mark ScaleTo +@implementation ScaleTo ++(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]) ) + return nil; + + 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]) ) + return nil; + + endScaleX = sx; + endScaleY = sy; + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + Action *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] scaleX:endScaleX scaleY:endScaleY]; + return copy; +} + +-(void) start +{ + [super start]; + 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 ScaleBy +-(void) start +{ + [super start]; + deltaX = startScaleX * endScaleX - startScaleX; + deltaY = startScaleY * endScaleY - startScaleY; +} + +-(IntervalAction*) reverse +{ + return [ScaleBy actionWithDuration: duration scaleX: 1/endScaleX scaleY:1/endScaleY]; +} +@end + +// +// Blink +// +#pragma mark - +#pragma mark Blink +@implementation Blink ++(id) actionWithDuration: (ccTime) t blinks: (unsigned int) b +{ + return [[[ self alloc] initWithDuration: t blinks: b] autorelease]; +} + +-(id) initWithDuration: (ccTime) t blinks: (unsigned int) b +{ + if( ! (self=[super initWithDuration: t] ) ) + return nil; + times = b; + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + Action *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] blinks: times]; + return copy; +} + +-(void) update: (ccTime) t +{ + ccTime slice = 1.0f / times; + ccTime m = fmodf(t, slice); + [target setVisible: (m > slice/2) ? YES : NO]; +} + +-(IntervalAction*) reverse +{ + // return 'self' + return [Blink actionWithDuration: duration blinks: times]; +} +@end + +// +// FadeIn +// +#pragma mark - +#pragma mark FadeIn +@implementation FadeIn +-(void) update: (ccTime) t +{ + [(id) target setOpacity: 255 *t]; +} +-(IntervalAction*) reverse +{ + return [FadeOut actionWithDuration: duration]; +} +@end + +// +// FadeOut +// +#pragma mark - +#pragma mark FadeOut +@implementation FadeOut +-(void) update: (ccTime) t +{ + [(id) target setOpacity: 255 *(1-t)]; +} +-(IntervalAction*) reverse +{ + return [FadeIn actionWithDuration: duration]; +} +@end + +// +// FadeTo +// +#pragma mark - +#pragma mark FadeTo +@implementation FadeTo ++(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 +{ + Action *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] opacity: toOpacity]; + return copy; +} + +-(void) start +{ + [super start]; + fromOpacity = [(id)target opacity]; +} + +-(void) update: (ccTime) t +{ + [(id)target setOpacity: fromOpacity + ( toOpacity - fromOpacity ) * t]; +} +@end + +// +// TintTo +// +#pragma mark - +#pragma mark TintTo +@implementation TintTo ++(id) actionWithDuration:(ccTime)t red:(GLubyte)r green:(GLubyte)g blue:(GLubyte)b +{ + return [[(TintTo*)[ 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 +{ + Action *copy = [(TintTo*)[[self class] allocWithZone: zone] initWithDuration: [self duration] red:to.r green:to.g blue:to.b]; + return copy; +} + +-(void) start +{ + [super start]; + + 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 TintBy ++(id) actionWithDuration:(ccTime)t red:(GLshort)r green:(GLshort)g blue:(GLshort)b +{ + return [[(TintBy*)[ 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[(TintBy*)[[self class] allocWithZone: zone] initWithDuration: [self duration] red:deltaR green:deltaG blue:deltaB]; +} + +-(void) start +{ + [super start]; + + 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)]; +} +- (IntervalAction*) reverse +{ + return [TintBy actionWithDuration:duration red:-deltaR green:-deltaG blue:-deltaB]; +} +@end + +// +// DelayTime +// +#pragma mark - +#pragma mark DelayTime +@implementation DelayTime +-(void) update: (ccTime) t +{ + return; +} + +-(id)reverse +{ + return [DelayTime actionWithDuration:duration]; +} +@end + +// +// ReverseTime +// +#pragma mark - +#pragma mark ReverseTime +@implementation ReverseTime ++(id) actionWithAction: (FiniteTimeAction*) action +{ + // casting to prevent warnings + ReverseTime *a = [super alloc]; + return [[a initWithAction:action] autorelease]; +} + +-(id) initWithAction: (FiniteTimeAction*) action +{ + if( !(self=[super initWithDuration: [action duration]]) ) + return nil; + + 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) start +{ + [super start]; + [other setTarget: target]; + [other start]; +} + +-(void) stop +{ + [other stop]; + [super stop]; +} + +-(void) update:(ccTime)t +{ + [other update:1-t]; +} + +-(IntervalAction*) reverse +{ + return [[other copy] autorelease]; +} +@end + +// +// Animate +// +#pragma mark - +#pragma mark Animate +@implementation Animate + ++(id) actionWithAnimation: (id)anim +{ + return [[[self alloc] initWithAnimation:anim restoreOriginalFrame:YES] autorelease]; +} + ++(id) actionWithAnimation: (id)anim restoreOriginalFrame:(BOOL)b +{ + return [[[self alloc] initWithAnimation:anim restoreOriginalFrame:b] autorelease]; +} + +-(id) initWithAnimation: (id)anim +{ + NSAssert( anim!=nil, @"Animate: argument Animation must be non-nil"); + return [self initWithAnimation:anim restoreOriginalFrame:YES]; +} + +-(id) initWithAnimation: (id)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; + animation = [anim retain]; + origFrame = nil; + } + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + return [[[self class] allocWithZone: zone] initWithAnimation: animation]; +} + +-(void) dealloc +{ + [animation release]; + [origFrame release]; + [super dealloc]; +} + +-(void) start +{ + [super start]; + id sprite = (id) target; + + [origFrame release]; + + origFrame = [[sprite displayFrame] retain]; +} + +-(void) stop +{ + if( restoreOriginalFrame ) { + id sprite = (id) target; + [sprite setDisplayFrame:origFrame]; + } + + [super stop]; +} + +-(void) update: (ccTime) t +{ + NSUInteger idx=0; + + ccTime slice = 1.0f / [[animation frames] count]; + + if(t !=0 ) + idx = t/ slice; + + if( idx >= [[animation frames] count] ) { + idx = [[animation frames] count] -1; + } + id sprite = (id) target; + if (! [sprite isFrameDisplayed: [[animation frames] objectAtIndex: idx]] ) { + [sprite setDisplayFrame: [[animation frames] objectAtIndex:idx]]; + } +} +@end diff --git a/cocos2d/Label.h b/cocos2d/Label.h new file mode 100644 index 0000000..c839012 --- /dev/null +++ b/cocos2d/Label.h @@ -0,0 +1,50 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import + +#import "Support/Texture2D.h" + +#import "TextureNode.h" + +/** Label is a subclass of TextureNode that knows how to render text labels + * + * All features from TextureNode are valid in Label + * + * Label are slow. Consider using LabelAtlas or BitmapFontAtlas instead. + */ +@interface Label : TextureNode +{ + CGSize _dimensions; + UITextAlignment _alignment; + NSString * _fontName; + CGFloat _fontSize; +} + +/** creates a label from a fontname, alignment, dimension and font size */ ++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(UITextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size; +/** creates a label from a fontname and font size */ ++ (id) labelWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size; +/** initializes the label with a font name, alignment, dimension and font size */ +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(UITextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size; +/** initializes the label with a font name and font size */ +- (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 Label. To obtain better performance use LabelAtlas + */ +- (void) setString:(NSString*)string; + +@end diff --git a/cocos2d/Label.m b/cocos2d/Label.m new file mode 100644 index 0000000..d0aa2da --- /dev/null +++ b/cocos2d/Label.m @@ -0,0 +1,86 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import "Label.h" +#import "Support/CGPointExtension.h" + +@implementation Label + +- (id) init +{ + NSException* myException = [NSException + exceptionWithName:@"LabelInit" + reason:@"Use initWithString:dimensions:aligment:fontName:font instead" + userInfo:nil]; + @throw myException; +} + ++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(UITextAlignment)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*)string dimensions:(CGSize)dimensions alignment:(UITextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size; +{ + if( (self=[super init]) ) { + + _dimensions = dimensions; + _alignment = alignment; + _fontName = [name retain]; + _fontSize = size; + + [self setString:string]; + } + return self; +} + +- (id) initWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size; +{ + if( (self=[super init]) ) { + + _dimensions = CGSizeZero; + _fontName = [name retain]; + _fontSize = size; + + [self setString:string]; + } + return self; +} + +- (void) setString:(NSString*)string +{ + if( CGSizeEqualToSize( _dimensions, CGSizeZero ) ) + // WARNING: double retain + self.texture = [[Texture2D alloc] initWithString:string fontName:_fontName fontSize:_fontSize]; + else + // WARNING: double retain + self.texture = [[Texture2D alloc] initWithString:string dimensions:_dimensions alignment:_alignment fontName:_fontName fontSize:_fontSize]; + + // end of warning. 1 retain only + [self.texture release]; +} + +- (void) dealloc +{ + [_fontName release]; + [super dealloc]; +} +@end diff --git a/cocos2d/LabelAtlas.h b/cocos2d/LabelAtlas.h new file mode 100644 index 0000000..a12f1da --- /dev/null +++ b/cocos2d/LabelAtlas.h @@ -0,0 +1,43 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "AtlasNode.h" +#import "TextureAtlas.h" + +/** LabelAtlas is a subclass of AtlasNode. + + It can be as a replacement of Label since it is MUCH faster. + + LabelAtlas versus Label: + - LabelAtlas is MUCH faster than Label + - LabelAtlas "characters" have a fixed height and width + - LabelAtlas "characters" can be anything you want since they are taken from an image file + + A more flexible class is BitmapFontAtlas. It supports variable width characters and it also has a nice editor. + */ +@interface LabelAtlas : AtlasNode { + + // string to render + NSString *string_; + + // the first char in the charmap + char mapStartChar; +} + +/** creates the LabelAtlas with a string, a char map file(the atlas), the width and height of each element and the starting char of the atlas */ ++(id) labelAtlasWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c; + +/** initializes the LabelAtlas with a string, a char map file(the atlas), the width and height 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/cocos2d/LabelAtlas.m b/cocos2d/LabelAtlas.m new file mode 100644 index 0000000..cf43821 --- /dev/null +++ b/cocos2d/LabelAtlas.m @@ -0,0 +1,136 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "LabelAtlas.h" +#import "ccMacros.h" + + +@implementation LabelAtlas + +#pragma mark LabelAtlas - Creation & Init ++(id) labelAtlasWithString:(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]; +} + + +-(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 LabelAtlas - Atlas generation + +-(void) updateAtlasValues +{ + int n = [string_ length]; + + ccV3F_C4B_T2F_Quad quad; + + const char *s = [string_ UTF8String]; + + for( int i=0; i textureAtlas_.totalQuads ) + [textureAtlas_ resizeCapacity: newString.length]; + + [string_ release]; + string_ = [newString retain]; + [self updateAtlasValues]; + + CGSize s; + s.width = [string_ length] * itemWidth; + s.height = itemHeight; + [self setContentSize:s]; +} + +#pragma mark LabelAtlas - draw + +// XXX: overriding draw from AtlasNode +- (void) draw +{ + glEnableClientState( GL_VERTEX_ARRAY); + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + + glEnable( GL_TEXTURE_2D); + + glColor4ub( color_.r, color_.g, color_.b, opacity_); + + BOOL newBlend = NO; + if( blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST ) { + newBlend = YES; + glBlendFunc( blendFunc_.src, blendFunc_.dst ); + } + + [textureAtlas_ drawNumberOfQuads: string_.length]; + + if( newBlend ) + glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + + // is this chepear than saving/restoring color state ? + glColor4ub( 255, 255, 255, 255); + + glDisable( GL_TEXTURE_2D); + + glDisableClientState(GL_VERTEX_ARRAY ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); +} +@end diff --git a/cocos2d/Layer.h b/cocos2d/Layer.h new file mode 100644 index 0000000..89b2a1d --- /dev/null +++ b/cocos2d/Layer.h @@ -0,0 +1,120 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import + +#import "CocosNode.h" +#import "TouchDelegateProtocol.h" + +// +// Layer +// +/** Layer is a subclass of CocosNode that implements the TouchEventsDelegate protocol. + + All features from CocosNode are valid, plus the following new features: + - It can receive iPhone Touches + - It can receive Accelerometer input +*/ +@interface Layer : CocosNode +{ + BOOL isTouchEnabled; + BOOL isAccelerometerEnabled; +} + +/** If isTouchEnabled, this method is called onEnter. Override it to change the + way Layer receives touch events. + ( Default: [[TouchDispatcher sharedDispatcher] addStandardDelegate:self priority:0] ) + Example: + -(void) registerWithTouchDispatcher + { + [[TouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES]; + } + */ +-(void) registerWithTouchDispatcher; + +// whether or not it will receive Touch events +@property(nonatomic,assign) BOOL isTouchEnabled; +// whether or not it will receive Accelerometer events +@property(nonatomic,assign) BOOL isAccelerometerEnabled; + +@end + +// +// ColorLayer +// +/** ColorLayer is a subclass of Layer that implements the CocosNodeRGBA protocol. + + All features from Layer are valid, plus the following new features: + - opacity + - RGB colors + */ +@interface ColorLayer : Layer +{ + GLubyte opacity_; + ccColor3B color_; + GLfloat squareVertices[4 * 2]; + GLubyte squareColors[4 * 4]; +} + +/** creates the Layer with color, width and height */ ++ (id) layerWithColor: (ccColor4B)color width:(GLfloat)w height:(GLfloat)h; +/** creates the layer with color. Width and height are the window size. */ ++ (id) layerWithColor: (ccColor4B)color; + +/** initializes a Layer with color, width and height */ +- (id) initWithColor:(ccColor4B)color width:(GLfloat)w height:(GLfloat)h; +/** initializes a Layer with color. Width and height are the window size. */ +- (id) initWithColor:(ccColor4B)color; + +/** change width */ +-(void) changeWidth: (GLfloat)w; +/** change height */ +-(void) changeHeight: (GLfloat)h; +/** change width and height + @since v0.8 + */ +-(void) changeWidth:(GLfloat)w height:(GLfloat)h; + +/** Opacity: conforms to CocosNodeRGBA protocol */ +@property (nonatomic,readonly) GLubyte opacity; +/** Opacity: conforms to CocosNodeRGBA protocol */ +@property (nonatomic,readonly) ccColor3B color; + +@end + +/** A Layer with the ability to multiplex it's children. + Features: + - It supports one or more children + - Only one children will be active a time + */ +@interface MultiplexLayer : Layer +{ + unsigned int enabledLayer; + NSMutableArray *layers; +} + +/** creates a MultiplexLayer with one or more layers using a variable argument list. */ ++(id) layerWithLayers: (Layer*) layer, ... NS_REQUIRES_NIL_TERMINATION; +/** initializes a MultiplexLayer with one or more layers using a variable argument list. */ +-(id) initWithLayers: (Layer*) 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 diff --git a/cocos2d/Layer.m b/cocos2d/Layer.m new file mode 100644 index 0000000..2d2d75d --- /dev/null +++ b/cocos2d/Layer.m @@ -0,0 +1,325 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import +#import + +#import "Layer.h" +#import "Director.h" +#import "TouchDispatcher.h" +#import "ccMacros.h" +#import "Support/CGPointExtension.h" + +#pragma mark - +#pragma mark Layer + +@implementation Layer + +#pragma mark Layer - Init +-(id) init +{ + if( (self=[super init]) ) { + + CGSize s = [[Director sharedDirector] winSize]; + anchorPoint_ = ccp(0.5f, 0.5f); + [self setContentSize:s]; + self.relativeAnchorPoint = NO; + + isTouchEnabled = NO; + isAccelerometerEnabled = NO; + } + + return self; +} + +#pragma mark Layer - Touch and Accelerometer related + +-(void) registerWithTouchDispatcher +{ + [[TouchDispatcher 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 + [[TouchDispatcher sharedDispatcher] removeDelegate:self]; + } + } +} + +#pragma mark Layer - Callbacks +-(void) onEnter +{ + // register 'parent' nodes first + // since events are propagated in reverse order + if (isTouchEnabled) + [self registerWithTouchDispatcher]; + + // then iterate over all the children + [super onEnter]; + + if( isAccelerometerEnabled ) + [[UIAccelerometer sharedAccelerometer] setDelegate:self]; +} + +-(void) onExit +{ + if( isTouchEnabled ) + [[TouchDispatcher sharedDispatcher] removeDelegate:self]; + + if( isAccelerometerEnabled ) + [[UIAccelerometer sharedAccelerometer] setDelegate:nil]; + + [super onExit]; +} +-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event +{ + NSAssert(NO, @"Layer#ccTouchBegan override me"); + return YES; +} +@end + +#pragma mark - +#pragma mark ColorLayer + +@interface ColorLayer (Private) +-(void) updateColor; +@end + +@implementation ColorLayer + +// Opacity and RGB color protocol +@synthesize opacity=opacity_, color=color_; + + +- (id) init +{ + NSException* myException = [NSException + exceptionWithName:@"ColorLayerInit" + reason:@"Use ColorLayer initWithColor instead" + userInfo:nil]; + @throw myException; +} + ++ (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 [[[self alloc] initWithColor:color] autorelease]; +} + +- (id) initWithColor:(ccColor4B)color width:(GLfloat)w height:(GLfloat) h +{ + if( (self=[super init]) ) { + color_.r = color.r; + color_.g = color.g; + color_.b = color.b; + opacity_ = color.a; + + for (NSUInteger i=0; i + +#import "MenuItem.h" +#import "Layer.h" + +typedef enum { + kMenuStateWaiting, + kMenuStateTrackingTouch +} MenuState; + +/** A Menu + * + * Features and Limitation: + * - You can add MenuItem objects in runtime using addChild: + * - But the only accecpted children are MenuItem objects + */ +@interface Menu : Layer +{ + MenuState state; + MenuItem *selectedItem; + GLubyte opacity_; + ccColor3B color_; +} + +/** creates a menu with it's items */ ++ (id) menuWithItems: (MenuItem*) item, ... NS_REQUIRES_NIL_TERMINATION; + +/** initializes a menu with it's items */ +- (id) initWithItems: (MenuItem*) 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 CocosNodeRGBA protocol */ +@property (nonatomic,readonly) GLubyte opacity; +/** conforms to CocosNodeRGBA protocol */ +@property (nonatomic,readonly) ccColor3B color; + +@end diff --git a/cocos2d/Menu.m b/cocos2d/Menu.m new file mode 100644 index 0000000..b7f7d13 --- /dev/null +++ b/cocos2d/Menu.m @@ -0,0 +1,456 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import "Menu.h" +#import "Director.h" +#import "TouchDispatcher.h" +#import "Support/CGPointExtension.h" + +enum { + kDefaultPadding = 5, +}; + +@interface Menu (Private) +// returns touched menu item, if any +-(MenuItem *) itemForTouch: (UITouch *) touch; +@end + +@implementation Menu + +@synthesize opacity=opacity_, color=color_; + +- (id) init +{ + NSException* myException = [NSException + exceptionWithName:@"MenuInit" + reason:@"Use initWithItems instead" + userInfo:nil]; + @throw myException; +} + ++(id) menuWithItems: (MenuItem*) item, ... +{ + va_list args; + va_start(args,item); + + id s = [[[self alloc] initWithItems: item vaList:args] autorelease]; + + va_end(args); + return s; +} + +-(id) initWithItems: (MenuItem*) item vaList: (va_list) args +{ + if( (self=[super init]) ) { + + self.isTouchEnabled = YES; + + // menu in the center of the screen + CGSize s = [[Director sharedDirector] winSize]; + + self.relativeAnchorPoint = 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 + CGRect r = [[UIApplication sharedApplication] statusBarFrame]; + ccDeviceOrientation orientation = [[Director sharedDirector] deviceOrientation]; + if( orientation == CCDeviceOrientationLandscapeLeft || orientation == CCDeviceOrientationLandscapeRight ) + s.height -= r.size.width; + else + s.height -= r.size.height; + self.position = ccp(s.width/2, s.height/2); + + int z=0; + + if (item) { + [self addChild: item z:z]; + MenuItem *i = va_arg(args, MenuItem*); + while(i) { + z++; + [self addChild: i z:z]; + i = va_arg(args, MenuItem*); + } + } + // [self alignItemsVertically]; + + selectedItem = nil; + state = kMenuStateWaiting; + } + + return self; +} + +-(void) dealloc +{ + [super dealloc]; +} + +/* + * override add: + */ +-(id) addChild:(MenuItem*)child z:(int)z tag:(int) aTag +{ + NSAssert( [child isKindOfClass:[MenuItem class]], @"Menu only supports MenuItem objects as children"); + return [super addChild:child z:z tag:aTag]; +} + +#pragma mark Menu - Events + +//- (BOOL)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +//{ +// UITouch *touch = [touches anyObject]; +// MenuItem *item = [self itemForTouch:touch]; +// +// if( item ) { +// [item selected]; +// selectedItem = item; +// return kEventHandled; +// } +// +// return kEventIgnored; +//} +// +//- (BOOL)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +//{ +// UITouch *touch = [touches anyObject]; +// MenuItem *item = [self itemForTouch:touch]; +// +// if( item ) { +// [item unselected]; +// [item activate]; +// return kEventHandled; +// +// } else if( selectedItem ) { +// [selectedItem unselected]; +// selectedItem = nil; +// +// // don't return kEventHandled here, since we are not handling it! +// } +// return kEventIgnored; +//} +// +//- (BOOL)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +//{ +// UITouch *touch = [touches anyObject]; +// MenuItem *item = [self itemForTouch:touch]; +// +// // "mouse" draged inside a button +// if( item ) { +// if( item != selectedItem ) { +// if( selectedItem ) +// [selectedItem unselected]; +// [item selected]; +// selectedItem = item; +// return kEventHandled; +// } +// +// // "mouse" draged outside the selected button +// } else { +// if( selectedItem ) { +// [selectedItem unselected]; +// selectedItem = nil; +// +// // don't return kEventHandled here, since we are not handling it! +// } +// } +// +// return kEventIgnored; +//} + +-(void) registerWithTouchDispatcher +{ + [[TouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES]; +} + +-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event +{ + if( state != kMenuStateWaiting ) return NO; + + selectedItem = [self itemForTouch:touch]; + [selectedItem selected]; + + if( selectedItem ) { + state = kMenuStateTrackingTouch; + return YES; + } + return NO; +} + +-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event +{ + NSAssert(state == kMenuStateTrackingTouch, @"[Menu ccTouchEnded] -- invalid state"); + + [selectedItem unselected]; + [selectedItem activate]; + + state = kMenuStateWaiting; +} + +-(void) ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event +{ + NSAssert(state == kMenuStateTrackingTouch, @"[Menu ccTouchCancelled] -- invalid state"); + + [selectedItem unselected]; + + state = kMenuStateWaiting; +} + +-(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event +{ + NSAssert(state == kMenuStateTrackingTouch, @"[Menu ccTouchMoved] -- invalid state"); + + MenuItem *currentItem = [self itemForTouch:touch]; + + if (currentItem != selectedItem) { + [selectedItem unselected]; + selectedItem = currentItem; + [selectedItem selected]; + } +} + +#pragma mark Menu - Alignment +-(void) alignItemsVertically +{ + return [self alignItemsVerticallyWithPadding:kDefaultPadding]; +} +-(void) alignItemsVerticallyWithPadding:(float)padding +{ + float height = -padding; + for(MenuItem *item in children) + height += [item contentSize].height * item.scaleY + padding; + + float y = height / 2.0f; + for(MenuItem *item in children) { + [item setPosition:ccp(0, y - [item contentSize].height * item.scaleY / 2.0f)]; + y -= [item contentSize].height * item.scaleY + padding; + } +} + +-(void) alignItemsHorizontally +{ + return [self alignItemsHorizontallyWithPadding:kDefaultPadding]; +} + +-(void) alignItemsHorizontallyWithPadding:(float)padding +{ + + float width = -padding; + for(MenuItem* item in children) + width += [item contentSize].width * item.scaleX + padding; + + float x = -width / 2.0f; + for(MenuItem* item in children) { + [item setPosition:ccp(x + [item contentSize].width * item.scaleX / 2.0f, 0)]; + x += [item contentSize].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; + for(MenuItem *item in children) { + 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 = [[Director sharedDirector] winSize]; + + row = 0; rowHeight = 0; rowColumns = 0; + float w, x, y = height / 2; + for(MenuItem *item in children) { + if(rowColumns == 0) { + rowColumns = [(NSNumber *) [rows objectAtIndex:row] unsignedIntegerValue]; + w = winSize.width / (1 + rowColumns); + x = w; + } + + rowHeight = fmaxf(rowHeight, [item contentSize].height); + [item setPosition:ccp(x - winSize.width / 2, + y - [item contentSize].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; + for(MenuItem *item in children) { + 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"); + + columnWidth = fmaxf(columnWidth, [item contentSize].width); + columnHeight += [item contentSize].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 = [[Director sharedDirector] winSize]; + + column = 0; columnWidth = 0; columnRows = 0; + float x = -width / 2, y; + for(MenuItem *item in children) { + if(columnRows == 0) { + columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue]; + y = ([(NSNumber *) [columnHeights objectAtIndex:column] intValue] + winSize.height) / 2; + } + + columnWidth = fmaxf(columnWidth, [item contentSize].width); + [item setPosition:ccp(x + [(NSNumber *) [columnWidths objectAtIndex:column] unsignedIntegerValue] / 2, + y - winSize.height / 2)]; + + y -= [item contentSize].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; + for(id item in children) + [item setOpacity:opacity_]; +} + +- (void) setRGB:(GLubyte)r:(GLubyte)g:(GLubyte)b +{ + color_.r=r; + color_.g=g; + color_.b=b; + for(id item in children) + [item setColor:color_]; +} + +-(void) setColor:(ccColor3B)color +{ + color_ = color; + for(id item in children) + [item setColor:color_]; +} + +#pragma mark Menu - Private + +-(MenuItem *) itemForTouch: (UITouch *) touch; +{ + CGPoint touchLocation = [touch locationInView: [touch view]]; + touchLocation = [[Director sharedDirector] convertCoordinate: touchLocation]; + + for( MenuItem* item in children ) { + CGPoint local = [item convertToNodeSpace:touchLocation]; + + CGRect r = [item rect]; + r.origin = CGPointZero; + + if( CGRectContainsPoint( r, local ) ) + return item; + } + return nil; +} +@end diff --git a/cocos2d/MenuItem.h b/cocos2d/MenuItem.h new file mode 100644 index 0000000..36de8b3 --- /dev/null +++ b/cocos2d/MenuItem.h @@ -0,0 +1,249 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import + +#import "CocosNode.h" + +@class Label; +@class LabelAtlas; +@class Sprite; + +#define kItemSize 32 + +/** Menu Item base class + * + * Subclass MenuItem (or any subclass) to create your custom MenuItem + */ +@interface MenuItem : CocosNode +{ + NSInvocation *invocation; + BOOL isEnabled; +} + +/** Creates a menu item with a target/selector */ ++(id) itemWithTarget:(id)target selector:(SEL)selector; + +/** Initializes a menu item with a target/selector */ +-(id) initWithTarget:(id)target selector:(SEL)selector; + +/** Returns the outside box */ +-(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 MenuItem */ +-(void) setIsEnabled:(BOOL)enabled; +/** Returns whether or not the MenuItem is enabled */ +-(BOOL) isEnabled; +@end + +/** An abstract class for "label" MenuItems + Any CocosNode that supports the CocosNodeLabel protocol can be added. + Supported nodes: + - BitmapFontAtlas + - LabelAtlas + - Label + */ +@interface MenuItemLabel : MenuItem +{ + CocosNode *label_; + ccColor3B colorBackup; + ccColor3B disabledColor_; +} + +/** the color that will be used to disable the item */ +@property (nonatomic,readwrite) ccColor3B disabledColor; + +/** Label that is rendered. It can be any CocosNode that implements the CocosNodeLabel */ +@property (nonatomic,readwrite,retain) CocosNode* label; + +/** creates a MenuItemLabel with a Label, target and selector */ ++(id) itemWithLabel:(CocosNode*)label target:(id)target selector:(SEL)selector; + +/** initializes a MenuItemLabel with a Label, target and selector */ +-(id) initWithLabel:(CocosNode*)label target:(id)target selector:(SEL)selector; + +/** sets a new string to the inner label */ +-(void) setString:(NSString*)label; + +/** Enable or disabled the MenuItemFont + @warning setIsEnabled changes the RGB color of the font + */ +-(void) setIsEnabled: (BOOL)enabled; +@end + +/** A MenuItemAtlasFont + Helper class that creates a MenuItemLabel class with a LabelAtlas + */ +@interface MenuItemAtlasFont : MenuItemLabel +{ +} + +/** 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; + + +@end + +/** A MenuItemFont + Helper class that creates a MenuItemLabel class with a Label + */ +@interface MenuItemFont : MenuItemLabel +{ +} +/** 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 MenuItemToggle */ ++(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; +@end + +/** MenuItemSprite accepts CocosNode objects as items. + The images has 3 different states: + - unselected image + - selected image + - disabled image + + @since v0.8.0 + */ +@interface MenuItemSprite : MenuItem +{ + BOOL selected; + CocosNode *normalImage_, *selectedImage_, *disabledImage_; +} + +/** the image used when the item is not selected */ +@property (nonatomic,readwrite,retain) CocosNode *normalImage; +/** the image used when the item is selected */ +@property (nonatomic,readwrite,retain) CocosNode *selectedImage; +/** the image used when the item is disabled */ +@property (nonatomic,readwrite,retain) CocosNode *disabledImage; + +/** creates a menu item with a normal and selected image*/ ++(id) itemFromNormalSprite:(CocosNode*)normalSprite selectedSprite:(CocosNode*)selectedSprite; +/** creates a menu item with a normal and selected image with target/selector */ ++(id) itemFromNormalSprite:(CocosNode*)normalSprite selectedSprite:(CocosNode*)selectedSprite target:(id)target selector:(SEL)selector; +/** creates a menu item with a normal,selected and disabled image with target/selector */ ++(id) itemFromNormalSprite:(CocosNode*)normalSprite selectedSprite:(CocosNode*)selectedSprite disabledSprite:(CocosNode*)disabledSprite target:(id)target selector:(SEL)selector; +/** initializes a menu item with a normal, selected and disabled image with target/selector */ +-(id) initFromNormalSprite:(CocosNode*)normalSprite selectedSprite:(CocosNode*)selectedSprite disabledSprite:(CocosNode*)disabledSprite target:(id)target selector:(SEL)selector; + +@end + +/** MenuItemAtlasCocosNode accepts AtlasCocosNode objects as items. + The images has 3 different states: + - unselected image + - selected image + - disabled image + + Limitations: + - AtlasSprite objects can only have as a parent an AltasSpriteManager + - So they need to be added twice: + - To the Menu + - And to the AtlasSpriteManager + - To respect the menu aligments, the AtlasSpriteManager should have the same coordinates as the Menu + @since v0.8.0 + */ +@interface MenuItemAtlasSprite : MenuItemSprite +{ +} +@end + +/** MenuItemImage 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 MenuItemImage : MenuItemSprite +{ +} + +/** 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; +@end + + + +/** A MenuItemToggle + A simple container class that "toggles" it's inner items + The inner itmes can be any MenuItem + */ +@interface MenuItemToggle : MenuItem +{ + NSUInteger selectedIndex_; + NSMutableArray* subItems_; + GLubyte opacity_; + ccColor3B color_; +} + +/** conforms with CocosNodeRGBA protocol */ +@property (nonatomic,readonly) GLubyte opacity; +/** conforms with CocosNodeRGBA 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:(MenuItem*) 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:(MenuItem*) item vaList:(va_list) args; + +/** return the selected item */ +-(MenuItem*) selectedItem; +@end + diff --git a/cocos2d/MenuItem.m b/cocos2d/MenuItem.m new file mode 100644 index 0000000..99db747 --- /dev/null +++ b/cocos2d/MenuItem.m @@ -0,0 +1,676 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "MenuItem.h" +#import "Label.h" +#import "LabelAtlas.h" +#import "IntervalAction.h" +#import "Sprite.h" +#import "AtlasSprite.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 MenuItem + +@implementation MenuItem + +-(id) init +{ + NSException* myException = [NSException + exceptionWithName:@"MenuItemInit" + reason:@"Init not supported. Use InitFromString" + userInfo:nil]; + @throw myException; +} + ++(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 class] instanceMethodSignatureForSelector:cb]; + + invocation = nil; + invocation = [NSInvocation invocationWithMethodSignature:sig]; + [invocation setTarget:rec]; + [invocation setSelector:cb]; + [invocation setArgument:&self atIndex:2]; + [invocation retain]; + } + + isEnabled = YES; + } + + return self; +} + +-(void) dealloc +{ + [invocation release]; + [super dealloc]; +} + +-(void) selected +{ + NSAssert(1,@"MenuItem.selected must be overriden"); +} + +-(void) unselected +{ + NSAssert(1,@"MenuItem.unselected must be overriden"); +} + +-(void) activate +{ + if(isEnabled) + [invocation invoke]; +} + +-(void) setIsEnabled: (BOOL)enabled +{ + isEnabled = enabled; +} + +-(BOOL) isEnabled +{ + return isEnabled; +} + +-(CGRect) rect +{ + return CGRectMake( self.position.x - contentSize_.width*anchorPoint_.x, self.position.y- + contentSize_.height*anchorPoint_.y, + contentSize_.width, contentSize_.height); +} +@end + + +#pragma mark - +#pragma mark MenuItemLabel + +@implementation MenuItemLabel + +@synthesize disabledColor = disabledColor_; + ++(id) itemWithLabel:(CocosNode*)label target:(id)target selector:(SEL)selector +{ + return [[[self alloc] initWithLabel:label target:target selector:selector] autorelease]; +} + +-(id) initWithLabel:(CocosNode*)label target:(id)target selector:(SEL)selector +{ + if( (self=[super initWithTarget:target selector:selector]) ) { + self.label = label; + colorBackup = ccWHITE; + disabledColor_ = ccc3( 126,126,126); + } + return self; +} + +-(CocosNode*) label +{ + return label_; +} +-(void) setLabel:(CocosNode*) label +{ + [label_ release]; + label_ = [label retain]; + [self setContentSize:[label_ contentSize]]; +} + +- (void) dealloc +{ + [label_ release]; + [super dealloc]; +} + +-(void) setString:(NSString *)string +{ + [label_ setString:string]; + [self setContentSize: [label_ contentSize]]; +} + +-(void) activate { + if(isEnabled) { + [self stopAllActions]; + + self.scale = 1.0f; + + [super activate]; + } +} + +-(void) selected +{ + // subclass to change the default action + if(isEnabled) { + [self stopActionByTag:kZoomActionTag]; + Action *zoomAction = [ScaleTo actionWithDuration:0.1f scale:1.2f]; + zoomAction.tag = kZoomActionTag; + [self runAction:zoomAction]; + } +} + +-(void) unselected +{ + // subclass to change the default action + if(isEnabled) { + [self stopActionByTag:kZoomActionTag]; + Action *zoomAction = [ScaleTo actionWithDuration:0.1f scale:1.0f]; + 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) draw +{ + [label_ draw]; +} + +- (void) setOpacity: (GLubyte)opacity +{ + [label_ setOpacity:opacity]; +} +-(GLubyte) opacity +{ + return [label_ opacity]; +} +- (void) setRGB:(GLubyte)r:(GLubyte)g:(GLubyte)b +{ + [label_ setColor: ccc3(r,g,b)]; +} +-(void) setColor:(ccColor3B)color +{ + [label_ setColor:color]; +} +-(ccColor3B) color +{ + return [label_ color]; +} +@end + +#pragma mark - +#pragma mark MenuItemAtlasFont + +@implementation MenuItemAtlasFont + ++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap +{ + return [MenuItemAtlasFont 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 lenght must be greater than 0"); + + LabelAtlas *label = [[LabelAtlas 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; +} + +-(void) dealloc +{ + [super dealloc]; +} +@end + + +#pragma mark - +#pragma mark MenuItemFont + +@implementation MenuItemFont + ++(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 lenght must be greater than 0"); + + Label *label = [Label labelWithString:value fontName:_fontName fontSize:_fontSize]; + + if((self=[super initWithLabel:label target:rec selector:cb]) ) { + // do something ? + } + + return self; +} + +-(void) dealloc +{ + [super dealloc]; +} +@end + +#pragma mark - +#pragma mark MenuItemSprite +@implementation MenuItemSprite + +@synthesize normalImage=normalImage_, selectedImage=selectedImage_, disabledImage=disabledImage_; + ++(id) itemFromNormalSprite:(CocosNode*)normalSprite selectedSprite:(CocosNode*)selectedSprite +{ + return [self itemFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil target:nil selector:nil]; +} ++(id) itemFromNormalSprite:(CocosNode*)normalSprite selectedSprite:(CocosNode*)selectedSprite target:(id)target selector:(SEL)selector +{ + return [self itemFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil target:target selector:selector]; +} ++(id) itemFromNormalSprite:(CocosNode*)normalSprite selectedSprite:(CocosNode*)selectedSprite disabledSprite:(CocosNode*)disabledSprite target:(id)target selector:(SEL)selector +{ + return [[[self alloc] initFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite target:target selector:selector] autorelease]; +} +-(id) initFromNormalSprite:(CocosNode*)normalSprite selectedSprite:(CocosNode*)selectedSprite disabledSprite:(CocosNode*)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; +} + +-(void) dealloc +{ + [normalImage_ release]; + [selectedImage_ release]; + [disabledImage_ release]; + + [super dealloc]; +} + +-(void) selected +{ + selected = YES; +} + +-(void) unselected +{ + selected = NO; +} + +-(void) draw +{ + if(isEnabled) { + if( selected ) + [selectedImage_ draw]; + else + [normalImage_ draw]; + + } else { + if(disabledImage_ != nil) + [disabledImage_ draw]; + + // disabled image was not provided + else + [normalImage_ draw]; + } +} + +#pragma mark MenuItemImage - CocosNodeRGBA 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]; +} +- (void) setRGB:(GLubyte)r:(GLubyte)g:(GLubyte)b +{ + [self setColor:ccc3(r,g,b)]; +} +-(GLubyte) opacity +{ + return [normalImage_ opacity]; +} +-(ccColor3B) color +{ + return [normalImage_ color]; +} +@end + +#pragma mark - +#pragma mark MenuItemAtlasSprite +@implementation MenuItemAtlasSprite + +-(id) initFromNormalSprite:(CocosNode*)normalSprite selectedSprite:(CocosNode*)selectedSprite disabledSprite:(CocosNode*)disabledSprite target:(id)target selector:(SEL)selector +{ + if( (self=[super initFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite target:target selector:selector]) ) { + + [normalImage_ setVisible:YES]; + [selectedImage_ setVisible:NO]; + [disabledImage_ setVisible:NO]; + } + return self; +} + +- (void)setPosition:(CGPoint)pos +{ + [super setPosition:pos]; + [normalImage_ setPosition:pos]; + [selectedImage_ setPosition:pos]; + [disabledImage_ setPosition:pos]; +} + +- (void)setRotation:(float)angle +{ + [super setRotation:angle]; + [normalImage_ setRotation:angle]; + [selectedImage_ setRotation:angle]; + [disabledImage_ setRotation:angle]; +} + +- (void)setScale:(float)scale +{ + [super setScale:scale]; + [normalImage_ setScale:scale]; + [selectedImage_ setScale:scale]; + [disabledImage_ setScale:scale]; +} + +- (void)selected +{ + if( isEnabled ) { + [super selected]; + [normalImage_ setVisible:NO]; + [selectedImage_ setVisible:YES]; + [disabledImage_ setVisible:NO]; + } +} + +- (void)unselected +{ + if( isEnabled ) { + [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 { + [normalImage_ setVisible:NO]; + [selectedImage_ setVisible:NO]; + if( disabledImage_ ) + [disabledImage_ setVisible:YES]; + else + [normalImage_ setVisible:YES]; + } +} + +-(void) draw +{ + // override parent draw + // since AtlasSpriteManager is the one that draws all the AtlasSprite objects +} +@end + + +#pragma mark - +#pragma mark MenuItemImage + +@implementation MenuItemImage + ++(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 +{ + CocosNode *normalImage = [Sprite spriteWithFile:normalI]; + CocosNode *selectedImage = [Sprite spriteWithFile:selectedI]; + CocosNode *disabledImage = nil; + + if(disabledI) + disabledImage = [Sprite spriteWithFile:disabledI]; + + return [self initFromNormalSprite:normalImage selectedSprite:selectedImage disabledSprite:disabledImage target:t selector:sel]; +} +@end + +#pragma mark - +#pragma mark MenuItemToggle + +// +// MenuItemToggle +// +@implementation MenuItemToggle + +@synthesize subItems = subItems_; +@synthesize opacity=opacity_, color=color_; + ++(id) itemWithTarget: (id)t selector: (SEL)sel items: (MenuItem*) 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:(MenuItem*) item vaList: (va_list) args +{ + if( (self=[super initWithTarget:t selector:sel]) ) { + + self.subItems = [NSMutableArray arrayWithCapacity:2]; + + int z = 0; + MenuItem *i = item; + while(i) { + z++; + [subItems_ addObject:i]; + i = va_arg(args, MenuItem*); + } + + selectedIndex_ = NSUIntegerMax; + [self setSelectedIndex:0]; + } + + return self; +} + +-(void) dealloc +{ + [subItems_ release]; + [super dealloc]; +} + +-(void)setSelectedIndex:(NSUInteger)index +{ + if( index != selectedIndex_ ) { + selectedIndex_=index; + [self removeChildByTag:kCurrentItem cleanup:NO]; + + MenuItem *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 +{ + [[subItems_ objectAtIndex:selectedIndex_] selected]; +} + +-(void) 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(MenuItem* item in subItems_) + [item setIsEnabled:enabled]; +} + +-(MenuItem*) selectedItem +{ + return [subItems_ objectAtIndex:selectedIndex_]; +} + +#pragma mark MenuItemToggle - CocosNodeRGBA protocol + +- (void) setOpacity: (GLubyte)opacity +{ + opacity_ = opacity; + for(MenuItem* item in subItems_) + [item setOpacity:opacity]; +} + +- (void) setColor:(ccColor3B)color +{ + color_ = color; + for(MenuItem* item in subItems_) + [item setColor:color]; +} + +- (void) setRGB:(GLubyte)r:(GLubyte)g:(GLubyte)b +{ + [self setColor:ccc3(r,g,b)]; +} +@end diff --git a/cocos2d/MotionStreak.h b/cocos2d/MotionStreak.h new file mode 100644 index 0000000..33de65f --- /dev/null +++ b/cocos2d/MotionStreak.h @@ -0,0 +1,56 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008, 2009 by Jason Booth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import +#import "CocosNode.h" +#import "Ribbon.h" + +/** + * 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. + * + * Limitations: + * MotionStreak, 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 MotionStreak : CocosNode +{ + Ribbon* ribbon_; + float mSegThreshold; + float mWidth; + CGPoint mLastLocation; +} + +/** Ribbon used by MotionStreak (weak reference) */ +@property (nonatomic,readonly) Ribbon *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/cocos2d/MotionStreak.m b/cocos2d/MotionStreak.m new file mode 100644 index 0000000..4e56d74 --- /dev/null +++ b/cocos2d/MotionStreak.m @@ -0,0 +1,92 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008, 2009 Jason Booth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + ********************************************************* + * + * 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 "MotionStreak.h" +#import "Support/CGPointExtension.h" + +@implementation MotionStreak + +@synthesize ribbon=ribbon_; + ++(id)streakWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color +{ + return [[[MotionStreak 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])) { + mSegThreshold = seg; + mWidth = width; + mLastLocation = CGPointZero; + ribbon_ = [Ribbon ribbonWithWidth: mWidth image:path length:length color:color fade:fade]; + [self addChild:ribbon_]; + + // update ribbon position + [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 = sqrtf(powf(mLastLocation.x - location.x, 2) + powf(mLastLocation.y - location.y, 2)); + if (len > mSegThreshold) + { + [ribbon_ addPointAt:location width:mWidth]; + mLastLocation = location; + } + [ribbon_ update:delta]; +} + + +-(void)dealloc +{ + [super dealloc]; +} + +#pragma mark MotionStreak - CocosNodeTexture protocol + +-(void) setTexture:(Texture2D*) texture +{ + [ribbon_ setTexture: texture]; +} + +-(Texture2D*) texture +{ + return [ribbon_ texture]; +} + +-(ccBlendFunc) blendFunc +{ + return [ribbon_ blendFunc]; +} + +-(void) setBlendFunc:(ccBlendFunc)blendFunc +{ + [ribbon_ setBlendFunc:blendFunc]; +} + +@end diff --git a/cocos2d/ParallaxNode.h b/cocos2d/ParallaxNode.h new file mode 100644 index 0000000..d265a26 --- /dev/null +++ b/cocos2d/ParallaxNode.h @@ -0,0 +1,37 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "CocosNode.h" +#import "Support/ccArray.h" + +/** ParallaxNode: A node that simulates a parallax scroller + + The children will be moved faster / slower than the parent according the the parallax ratio. + + */ +@interface ParallaxNode : CocosNode { + 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 + */ +-(id) addChild: (CocosNode*)node z:(int)z parallaxRatio:(CGPoint)c positionOffset:(CGPoint)positionOffset; + +@end diff --git a/cocos2d/ParallaxNode.m b/cocos2d/ParallaxNode.m new file mode 100644 index 0000000..06993b7 --- /dev/null +++ b/cocos2d/ParallaxNode.m @@ -0,0 +1,153 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "ParallaxNode.h" +#import "Support/CGPointExtension.h" +#import "Support/ccArray.h" + +@interface CGPointObject : NSObject +{ + CGPoint ratio_; + CGPoint offset_; + CocosNode *child_; // weak ref +} +@property (readwrite) CGPoint ratio; +@property (readwrite) CGPoint offset; +@property (readwrite,assign) CocosNode *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 ParallaxNode + +@synthesize parallaxArray=parallaxArray_; + +-(id) init +{ + if( (self=[super init]) ) { + parallaxArray_ = ccArrayNew(5); + +// [self schedule:@selector(updateCoords:)]; + lastPosition = CGPointMake(-100,-100); + } + return self; +} + +- (void) dealloc +{ + if( parallaxArray_ ) { + ccArrayFree(parallaxArray_); + parallaxArray_ = nil; + } + [super dealloc]; +} + +-(id) addChild:(CocosNode*)child z:(int)z tag:(int)tag +{ + NSAssert(NO,@"ParallaxNode: use addChild:z:parallaxRatio:positionOffset instead"); + return nil; +} + +-(id) addChild: (CocosNode*) child z:(int)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; + ccArrayAppendObject(parallaxArray_, obj); + + CGPoint pos = self.position; + float x = pos.x * ratio.x + offset.x; + float y = pos.y * ratio.y + offset.y; + child.position = ccp(x,y); + + return [super addChild: child z:z tag:child.tag]; +} + +-(void) removeChild:(CocosNode*)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_; + + CocosNode *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/cocos2d/ParticleExamples.h b/cocos2d/ParticleExamples.h new file mode 100644 index 0000000..5ac52fe --- /dev/null +++ b/cocos2d/ParticleExamples.h @@ -0,0 +1,82 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "PointParticleSystem.h" +#import "QuadParticleSystem.h" + +//! Fire particle system +@interface ParticleFire: PointParticleSystem +{ +} +@end + +//! Fireworks particle system +@interface ParticleFireworks : PointParticleSystem +{ +} +@end + +//! Sun particle system +@interface ParticleSun : PointParticleSystem +{ +} +@end + +//! Galaxy particle system +@interface ParticleGalaxy : PointParticleSystem +{ +} +@end + +//! Flower particle system +@interface ParticleFlower : PointParticleSystem +{ +} +@end + +//! Meteor particle system +@interface ParticleMeteor : PointParticleSystem +{ +} +@end + +//! Spiral particle system +@interface ParticleSpiral : PointParticleSystem +{ +} +@end + +//! Explosion particle system +@interface ParticleExplosion : PointParticleSystem +{ +} +@end + +//! Smoke particle system +@interface ParticleSmoke : PointParticleSystem +{ +} +@end + +//! Snow particle system +@interface ParticleSnow : PointParticleSystem +{ +} +@end + +//! Rain particle system +@interface ParticleRain : PointParticleSystem +{ +} +@end diff --git a/cocos2d/ParticleExamples.m b/cocos2d/ParticleExamples.m new file mode 100644 index 0000000..e648bc0 --- /dev/null +++ b/cocos2d/ParticleExamples.m @@ -0,0 +1,883 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +// cocos2d +#import "ParticleExamples.h" +#import "TextureMgr.h" +#import "Director.h" +#import "Support/CGPointExtension.h" + +// +// ParticleFireworks +// +@implementation ParticleFireworks +-(id) init +{ + return [self initWithTotalParticles:1500]; +} + +-(id) initWithTotalParticles:(int)p +{ + if( !(self=[super initWithTotalParticles:p]) ) + return nil; + + // duration + duration = -1; + + // gravity + gravity.x = 0; + gravity.y = -90; + + // angle + angle = 90; + angleVar = 20; + + // radial + radialAccel = 0; + radialAccelVar = 0; + + // speed of particles + speed = 180; + speedVar = 50; + + // emitter position + self.position = ccp(160, 160); + + // 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 = kParticleStartSizeEqualToEndSize; + + self.texture = [[TextureMgr sharedTextureMgr] addImage: @"fire.png"]; + + // additive + blendAdditive = NO; + + return self; +} +@end + +// +// ParticleFire +// +@implementation ParticleFire +-(id) init +{ + return [self initWithTotalParticles:250]; +} + +-(id) initWithTotalParticles:(int) p +{ + if( !(self=[super initWithTotalParticles:p]) ) + return nil; + + // duration + duration = -1; + + // gravity + gravity.x = 0; + gravity.y = 0; + + // angle + angle = 90; + angleVar = 10; + + // radial acceleration + radialAccel = 0; + radialAccelVar = 0; + + // emitter position + self.position = ccp(160, 60); + posVar = ccp(40, 20); + + // life of particles + life = 3; + lifeVar = 0.25f; + + // speed of particles + speed = 60; + speedVar = 20; + + // size, in pixels + startSize = 54.0f; + startSizeVar = 10.0f; + endSize = kParticleStartSizeEqualToEndSize; + + // 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 = [[TextureMgr sharedTextureMgr] addImage: @"fire.png"]; + + // additive + blendAdditive = YES; + + return self; +} +@end + +// +// ParticleSun +// +@implementation ParticleSun +-(id) init +{ + return [self initWithTotalParticles:350]; +} + +-(id) initWithTotalParticles:(int) p +{ + if( !(self=[super initWithTotalParticles:p]) ) + return nil; + + // additive + blendAdditive = YES; + + // duration + duration = -1; + + // gravity + gravity.x = 0; + gravity.y = 0; + + // angle + angle = 90; + angleVar = 360; + + // radial acceleration + radialAccel = 0; + radialAccelVar = 0; + + // emitter position + self.position = ccp(160, 240); + posVar = CGPointZero; + + // life of particles + life = 1; + lifeVar = 0.5f; + + // speed of particles + speed = 20; + speedVar = 5; + + // size, in pixels + startSize = 30.0f; + startSizeVar = 10.0f; + endSize = kParticleStartSizeEqualToEndSize; + + // 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 = [[TextureMgr sharedTextureMgr] addImage: @"fire.png"]; + + return self; +} +@end + +// +// ParticleGalaxy +// +@implementation ParticleGalaxy +-(id) init +{ + return [self initWithTotalParticles:200]; +} + +-(id) initWithTotalParticles:(int)p +{ + if( !(self=[super initWithTotalParticles:p]) ) + return nil; + + // duration + duration = -1; + + // gravity + gravity.x = 0; + gravity.y = 0; + + // angle + angle = 90; + angleVar = 360; + + // speed of particles + speed = 60; + speedVar = 10; + + // radial + radialAccel = -80; + radialAccelVar = 0; + + // tagential + tangentialAccel = 80; + tangentialAccelVar = 0; + + // emitter position + self.position = ccp(160, 240); + posVar = CGPointZero; + + // life of particles + life = 4; + lifeVar = 1; + + // size, in pixels + startSize = 37.0f; + startSizeVar = 10.0f; + endSize = kParticleStartSizeEqualToEndSize; + + // 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 = [[TextureMgr sharedTextureMgr] addImage: @"fire.png"]; + + // additive + blendAdditive = YES; + + return self; +} +@end + +// +// ParticleFlower +// +@implementation ParticleFlower +-(id) init +{ + return [self initWithTotalParticles:250]; +} + +-(id) initWithTotalParticles:(int) p +{ + if( !(self=[super initWithTotalParticles:p]) ) + return nil; + + // duration + duration = -1; + + // gravity + gravity.x = 0; + gravity.y = 0; + + // angle + angle = 90; + angleVar = 360; + + // speed of particles + speed = 80; + speedVar = 10; + + // radial + radialAccel = -60; + radialAccelVar = 0; + + // tagential + tangentialAccel = 15; + tangentialAccelVar = 0; + + // emitter position + self.position = ccp(160, 240); + posVar = CGPointZero; + + // life of particles + life = 4; + lifeVar = 1; + + // size, in pixels + startSize = 30.0f; + startSizeVar = 10.0f; + endSize = kParticleStartSizeEqualToEndSize; + + // 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 = [[TextureMgr sharedTextureMgr] addImage: @"fire.png"]; + + // additive + blendAdditive = YES; + + return self; +} +@end + +// +// ParticleMeteor +// +@implementation ParticleMeteor +-(id) init +{ + return [self initWithTotalParticles:150]; +} + +-(id) initWithTotalParticles:(int) p +{ + if( !(self=[super initWithTotalParticles:p]) ) + return nil; + + // duration + duration = -1; + + // gravity + gravity.x = -200; + gravity.y = 200; + + // angle + angle = 90; + angleVar = 360; + + // speed of particles + speed = 15; + speedVar = 5; + + // radial + radialAccel = 0; + radialAccelVar = 0; + + // tagential + tangentialAccel = 0; + tangentialAccelVar = 0; + + // emitter position + self.position = ccp(160, 240); + posVar = CGPointZero; + + // life of particles + life = 2; + lifeVar = 1; + + // size, in pixels + startSize = 60.0f; + startSizeVar = 10.0f; + endSize = kParticleStartSizeEqualToEndSize; + + // 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 = [[TextureMgr sharedTextureMgr] addImage: @"fire.png"]; + + // additive + blendAdditive = YES; + + return self; +} +@end + +// +// ParticleSpiral +// +@implementation ParticleSpiral +-(id) init +{ + return [self initWithTotalParticles:500]; +} + +-(id) initWithTotalParticles:(int) p +{ + if( !(self=[super initWithTotalParticles:p]) ) + return nil; + + // duration + duration = -1; + + // gravity + gravity.x = 0; + gravity.y = 0; + + // angle + angle = 90; + angleVar = 0; + + // speed of particles + speed = 150; + speedVar = 0; + + // radial + radialAccel = -380; + radialAccelVar = 0; + + // tagential + tangentialAccel = 45; + tangentialAccelVar = 0; + + // emitter position + self.position = ccp(160, 240); + posVar = CGPointZero; + + // life of particles + life = 12; + lifeVar = 0; + + // size, in pixels + startSize = 20.0f; + startSizeVar = 0.0f; + endSize = kParticleStartSizeEqualToEndSize; + + // 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 = [[TextureMgr sharedTextureMgr] addImage: @"fire.png"]; + + // additive + blendAdditive = NO; + + return self; +} +@end + +// +// ParticleExplosion +// +@implementation ParticleExplosion +-(id) init +{ + return [self initWithTotalParticles:700]; +} + +-(id) initWithTotalParticles:(int)p +{ + if( !(self=[super initWithTotalParticles:p]) ) + return nil; + + // duration + duration = 0.1f; + + // gravity + gravity.x = 0; + gravity.y = -100; + + // angle + angle = 90; + angleVar = 360; + + // speed of particles + speed = 70; + speedVar = 40; + + // radial + radialAccel = 0; + radialAccelVar = 0; + + // tagential + tangentialAccel = 0; + tangentialAccelVar = 0; + + // emitter position + self.position = ccp(160, 240); + posVar = CGPointZero; + + // life of particles + life = 5.0f; + lifeVar = 2; + + // size, in pixels + startSize = 15.0f; + startSizeVar = 10.0f; + endSize = kParticleStartSizeEqualToEndSize; + + // 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 = [[TextureMgr sharedTextureMgr] addImage: @"fire.png"]; + + // additive + blendAdditive = NO; + + return self; +} +@end + +// +// ParticleSmoke +// +@implementation ParticleSmoke +-(id) init +{ + return [self initWithTotalParticles:200]; +} + +-(id) initWithTotalParticles:(int) p +{ + if( !(self=[super initWithTotalParticles:p]) ) + return nil; + + // duration + duration = -1; + + // gravity + gravity.x = 0; + gravity.y = 0; + + // angle + angle = 90; + angleVar = 5; + + // radial acceleration + radialAccel = 0; + radialAccelVar = 0; + + // emitter position + self.position = ccp(160, 0); + posVar = ccp(20, 0); + + // life of particles + life = 4; + lifeVar = 1; + + // speed of particles + speed = 25; + speedVar = 10; + + // size, in pixels + startSize = 60.0f; + startSizeVar = 10.0f; + endSize = kParticleStartSizeEqualToEndSize; + + // 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 = [[TextureMgr sharedTextureMgr] addImage: @"fire.png"]; + + // additive + blendAdditive = NO; + + return self; +} +@end + +@implementation ParticleSnow +-(id) init +{ + return [self initWithTotalParticles:700]; +} + +-(id) initWithTotalParticles:(int)p +{ + if( !(self=[super initWithTotalParticles:p]) ) + return nil; + + // duration + duration = -1; + + // gravity + gravity.x = 0; + gravity.y = -1; + + // angle + angle = -90; + angleVar = 5; + + // speed of particles + speed = 5; + speedVar = 1; + + // radial + radialAccel = 0; + radialAccelVar = 1; + + // tagential + tangentialAccel = 0; + tangentialAccelVar = 1; + + // emitter position + self.position = (CGPoint) { + [[Director sharedDirector] winSize].width / 2, + [[Director sharedDirector] winSize].height + 10 + }; + posVar = ccp( [[Director sharedDirector] winSize].width / 2, 0 ); + + // life of particles + life = 45; + lifeVar = 15; + + // size, in pixels + startSize = 10.0f; + startSizeVar = 5.0f; + endSize = kParticleStartSizeEqualToEndSize; + + // 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 = [[TextureMgr sharedTextureMgr] addImage: @"fire.png"]; + + // additive + blendAdditive = NO; + + return self; +} +@end + +@implementation ParticleRain +-(id) init +{ + return [self initWithTotalParticles:1000]; +} + +-(id) initWithTotalParticles:(int)p +{ + if( !(self=[super initWithTotalParticles:p]) ) + return nil; + + // duration + duration = -1; + + // gravity + gravity.x = 10; + gravity.y = -10; + + // angle + angle = -90; + angleVar = 5; + + // speed of particles + speed = 130; + speedVar = 30; + + // radial + radialAccel = 0; + radialAccelVar = 1; + + // tagential + tangentialAccel = 0; + tangentialAccelVar = 1; + + // emitter position + self.position = (CGPoint) { + [[Director sharedDirector] winSize].width / 2, + [[Director sharedDirector] winSize].height + }; + posVar = ccp( [[Director sharedDirector] winSize].width / 2, 0 ); + + // life of particles + life = 4.5f; + lifeVar = 0; + + // size, in pixels + startSize = 4.0f; + startSizeVar = 2.0f; + endSize = kParticleStartSizeEqualToEndSize; + + // 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 = [[TextureMgr sharedTextureMgr] addImage: @"fire.png"]; + + // additive + blendAdditive = NO; + + return self; +} +@end diff --git a/cocos2d/ParticleSystem.h b/cocos2d/ParticleSystem.h new file mode 100644 index 0000000..c314dcf --- /dev/null +++ b/cocos2d/ParticleSystem.h @@ -0,0 +1,265 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import + +#import "CocosNode.h" +#import "ccTypes.h" + +enum { + kParticleStartSizeEqualToEndSize = -1, + kParticleDurationInfinity = -1, +}; + +/** possible types of particle positions */ +typedef enum { + kPositionTypeFree, + kPositionTypeGrouped +} tPositionType; + +/** Structure that contains the values of each particle + */ +typedef struct sParticle +{ + CGPoint pos; + CGPoint startPos; + CGPoint dir; + float radialAccel; + float tangentialAccel; + ccColor4F color; + ccColor4F deltaColor; + float size; + float deltaSize; + float angle; + float deltaAngle; + float life; +} Particle; + +@class Texture2D; + +/** Particle System base class + Attributes of a Particle System: + * duration + * gravity + * emmision rate + * total max particles + * angle +- variance + * speed +- variance + * tangential acceleration +- variance + * radial acceleration +- variance + * start size +- variance + * end size +- variance + * start color +- variance + * end color +- variance + * life +- variance + * blend additive or not + * one texture + + Limitations: + * size can't be bigger than 64 + * the system can't be scaled since the particles are rendered using GL_POINT_SPRITE + */ +@interface ParticleSystem : CocosNode +{ + int id; + + // 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; + + /// Gravity of the particles + CGPoint gravity; + + // position is from "superclass" CocosNode + // Emitter centerOfGravity position + CGPoint centerOfGravity; + // Position variance + CGPoint posVar; + + // The angle (direction) of the particles measured in degrees + float angle; + // Angle variance measured in degrees; + float angleVar; + + // 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; + + // 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 + Particle *particles; + // Maximum particles + int totalParticles; + // Count of active particles + int particleCount; + + // additive color or blend + BOOL blendAdditive; + // color modulate + BOOL colorModulate; + + // How many particles can be emitted per second + float emissionRate; + float emitCounter; + + // Texture of the particles + Texture2D *texture_; + // blend function + ccBlendFunc blendFunc_; + + // movment type: free or grouped + tPositionType positionType_; + + // Whether or not the node will be auto-removed when there are not particles + BOOL autoRemoveOnFinish_; + + // particle idx + int particleIdx; +} + +/** Is the emitter active */ +@property (nonatomic,readonly) BOOL active; +/** Quantity of particles that are being simulated at the moment */ +@property (nonatomic,readonly) int particleCount; +/** Gravity value */ +@property (nonatomic,readwrite,assign) CGPoint gravity; +/** How many seconds the emitter wil run. -1 means 'forever' */ +@property (nonatomic,readwrite,assign) float duration; +/** centerOfGravity of the emitter */ +@property (nonatomic,readwrite,assign) CGPoint centerOfGravity; +/** 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; +/** speed of each particle */ +@property (nonatomic,readwrite,assign) float speed; +/** speed variance of each particle */ +@property (nonatomic,readwrite,assign) float speedVar; +/** tangential acceleration of each particle */ +@property (nonatomic,readwrite,assign) float tangentialAccel; +/** tangential acceleration variance of each particle */ +@property (nonatomic,readwrite,assign) float tangentialAccelVar; +/** radial acceleration of each particle */ +@property (nonatomic,readwrite,assign) float radialAccel; +/** radial acceleration variance of each particle */ +@property (nonatomic,readwrite,assign) float radialAccelVar; +/** 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) int totalParticles; +/** conforms to CocosNodeTexture protocol */ +@property (nonatomic,readwrite, retain) Texture2D * texture; +/** conforms to CocosNodeTexture protocol */ +@property (nonatomic,readwrite) ccBlendFunc blendFunc; +/** whether or not the particles are using "blend additive */ +@property (nonatomic,readwrite) BOOL blendAdditive; +/** particles movement type: Free or Grouped + @since v0.8 + */ +@property (nonatomic,readwrite) tPositionType 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; + +//! Initializes a system with a fixed number of particles +-(id) initWithTotalParticles:(int) numberOfParticles; +//! Add a particle to the emitter +-(BOOL) addParticle; +//! Initializes a particle +-(void) initParticle: (Particle*) 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; +@end + diff --git a/cocos2d/ParticleSystem.m b/cocos2d/ParticleSystem.m new file mode 100644 index 0000000..57cedd6 --- /dev/null +++ b/cocos2d/ParticleSystem.m @@ -0,0 +1,234 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +// 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://love.sf.net + +// opengl +#import + +// cocos2d +#import "ParticleSystem.h" +#import "TextureMgr.h" +#import "ccMacros.h" + +// support +#import "Support/OpenGL_Internal.h" +#import "Support/CGPointExtension.h" + +@implementation ParticleSystem +@synthesize active, duration; +@synthesize centerOfGravity, posVar; +@synthesize particleCount; +@synthesize life, lifeVar; +@synthesize angle, angleVar; +@synthesize speed, speedVar; +@synthesize tangentialAccel, tangentialAccelVar; +@synthesize radialAccel, radialAccelVar; +@synthesize startColor, startColorVar, endColor, endColorVar; +@synthesize startSpin, startSpinVar, endSpin, endSpinVar; +@synthesize emissionRate; +@synthesize totalParticles; +@synthesize startSize, startSizeVar; +@synthesize endSize, endSizeVar; +@synthesize gravity; +@synthesize blendFunc = blendFunc_; +@synthesize blendAdditive; +@synthesize positionType = positionType_; +@synthesize autoRemoveOnFinish = autoRemoveOnFinish_; + +-(id) init { + NSException* myException = [NSException + exceptionWithName:@"Particle.init" + reason:@"Particle.init shall not be called. Use initWithTotalParticles instead." + userInfo:nil]; + @throw myException; +} + +-(id) initWithTotalParticles:(int) numberOfParticles +{ + if( (self=[super init]) ) { + + totalParticles = numberOfParticles; + + particles = malloc( sizeof(Particle) * totalParticles ); + + if( ! particles ) { + NSLog(@"Particle system: not enough memory"); + if( particles ) + free(particles); + return nil; + } + + bzero( particles, sizeof(Particle) * totalParticles ); + + // default, active + active = YES; + + // default: additive + blendAdditive = NO; + + // blend function + blendFunc_ = (ccBlendFunc) { CC_BLEND_SRC, CC_BLEND_DST }; + + // default movement type; + positionType_ = kPositionTypeFree; + + // default: modulate + // XXX: not used + // colorModulate = YES; + + autoRemoveOnFinish_ = NO; + + [self schedule:@selector(step:)]; + } + + return self; +} + +-(void) dealloc +{ + free( particles ); + + [texture_ release]; + + [super dealloc]; +} + +-(BOOL) addParticle +{ + if( [self isFull] ) + return NO; + + Particle * particle = &particles[ particleCount ]; + + [self initParticle: particle]; + particleCount++; + + return YES; +} + +-(void) initParticle: (Particle*) particle +{ + CGPoint v; + + // position + // XXX: source should be deprecated. + particle->pos.x = (int) (centerOfGravity.x + posVar.x * CCRANDOM_MINUS1_1()); + particle->pos.y = (int) (centerOfGravity.y + posVar.y * CCRANDOM_MINUS1_1()); + + // direction + float a = CC_DEGREES_TO_RADIANS( angle + angleVar * CCRANDOM_MINUS1_1() ); + v.y = sinf( a ); + v.x = cosf( a ); + float s = speed + speedVar * CCRANDOM_MINUS1_1(); + particle->dir = ccpMult( v, s ); + + // radial accel + particle->radialAccel = radialAccel + radialAccelVar * CCRANDOM_MINUS1_1(); + + // tangential accel + particle->tangentialAccel = tangentialAccel + tangentialAccelVar * CCRANDOM_MINUS1_1(); + + // life + particle->life = life + lifeVar * CCRANDOM_MINUS1_1(); + particle->life = MAX(0, particle->life); // no negative life + + // Color + ccColor4F start; + start.r = startColor.r + startColorVar.r * CCRANDOM_MINUS1_1(); + start.g = startColor.g + startColorVar.g * CCRANDOM_MINUS1_1(); + start.b = startColor.b + startColorVar.b * CCRANDOM_MINUS1_1(); + start.a = startColor.a + startColorVar.a * CCRANDOM_MINUS1_1(); + + ccColor4F end; + end.r = endColor.r + endColorVar.r * CCRANDOM_MINUS1_1(); + end.g = endColor.g + endColorVar.g * CCRANDOM_MINUS1_1(); + end.b = endColor.b + endColorVar.b * CCRANDOM_MINUS1_1(); + end.a = endColor.a + endColorVar.a * CCRANDOM_MINUS1_1(); + + particle->color = start; + particle->deltaColor.r = (end.r - start.r) / particle->life; + particle->deltaColor.g = (end.g - start.g) / particle->life; + particle->deltaColor.b = (end.b - start.b) / particle->life; + particle->deltaColor.a = (end.a - start.a) / particle->life; + + // size + float startS = startSize + startSizeVar * CCRANDOM_MINUS1_1(); + startS = MAX(0, startS); // no negative size + + particle->size = startS; + if( endSize == kParticleStartSizeEqualToEndSize ) + particle->deltaSize = 0; + else { + float endS = endSize + endSizeVar * CCRANDOM_MINUS1_1(); + particle->deltaSize = (endS - startS) / particle->life; + } + + // angle + float startA = startSpin + startSpinVar * CCRANDOM_MINUS1_1(); + float endA = endSpin + endSpinVar * CCRANDOM_MINUS1_1(); + particle->angle = startA; + particle->deltaAngle = (endA - startA) / particle->life; + + // position + particle->startPos = self.position; +} + +-(void) stopSystem +{ + active = NO; + elapsed = duration; + emitCounter = 0; +} + +-(void) resetSystem +{ + active = YES; + elapsed = 0; + for(particleIdx = 0; particleIdx < particleCount; ++particleIdx) { + Particle *p = &particles[particleIdx]; + p->life = 0; + } +} + +-(BOOL) isFull +{ + return (particleCount == totalParticles); +} + +#pragma mark ParticleSystem - CocosNodeTexture protocol + +-(void) setTexture:(Texture2D*) texture +{ + [texture_ release]; + texture_ = [texture retain]; + if( ! [texture hasPremultipliedAlpha] ) { + blendFunc_.src = GL_SRC_ALPHA; + blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; + } +} + +-(Texture2D*) texture +{ + return texture_; +} +@end + + diff --git a/cocos2d/PointParticleSystem.h b/cocos2d/PointParticleSystem.h new file mode 100644 index 0000000..e965aa4 --- /dev/null +++ b/cocos2d/PointParticleSystem.h @@ -0,0 +1,34 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "ParticleSystem.h" + +/** PointParticleSystem is a subclass of ParticleSystem + 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 + */ +@interface PointParticleSystem : ParticleSystem +{ + // Array of (x,y,size) + ccPointSprite *vertices; + // vertices buffer id + GLuint verticesID; +} +@end + diff --git a/cocos2d/PointParticleSystem.m b/cocos2d/PointParticleSystem.m new file mode 100644 index 0000000..1cbfc1f --- /dev/null +++ b/cocos2d/PointParticleSystem.m @@ -0,0 +1,257 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +// opengl +#import + +// cocos2d +#import "PointParticleSystem.h" +#import "TextureMgr.h" +#import "ccMacros.h" + +// support +#import "Support/OpenGL_Internal.h" +#import "Support/CGPointExtension.h" + +@implementation PointParticleSystem + +-(id) initWithTotalParticles:(int) numberOfParticles +{ + if( (self=[super initWithTotalParticles:numberOfParticles]) ) { + + vertices = malloc( sizeof(ccPointSprite) * totalParticles ); + + if( ! vertices ) { + NSLog(@"Particle system: not enough memory"); + if( vertices ) + free(vertices); + return nil; + } + + 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); + } + + return self; +} + +-(void) dealloc +{ + free(vertices); + glDeleteBuffers(1, &verticesID); + + [super dealloc]; +} + +// XXX +// XXX: All subclasses of ParticleSystem share this code +// XXX: so some parts of this coded should be moved to the base class +// XXX +// XXX: BUT the change shall NOT DROP a single FPS +// XXX: +-(void) step: (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; + + // test performance with [self absolutePosition]; +// CGPoint absolutePosition = [self convertToWorldSpace:CGPointZero]; + CGPoint absolutePosition = position_; + + while( particleIdx < particleCount ) + { + Particle *p = &particles[particleIdx]; + + if( p->life > 0 ) { + + 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->radialAccel); + + // tangential acceleration + float newy = tangential.x; + tangential.x = -tangential.y; + tangential.y = newy; + tangential = ccpMult(tangential, p->tangentialAccel); + + // (gravity + radial + tangential) * dt + tmp = ccpAdd( ccpAdd( radial, tangential), gravity); + tmp = ccpMult( tmp, dt); + p->dir = ccpAdd( p->dir, tmp); + tmp = ccpMult(p->dir, dt); + p->pos = ccpAdd( p->pos, tmp ); + + 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); + + p->size += (p->deltaSize * dt); + p->size = MAX( 0, p->size ); + + p->life -= dt; + + // + // update values in point + // + CGPoint newPos = p->pos; + if( positionType_ == kPositionTypeFree ) { + newPos = ccpSub(absolutePosition, p->startPos); + newPos = ccpSub( p->pos, newPos); + } + + // place vertices and colos in array + vertices[particleIdx].pos = newPos; + vertices[particleIdx].size = p->size; + vertices[particleIdx].colors = p->color; + + // update particle counter + particleIdx++; + + } else { + // life < 0 + if( particleIdx != particleCount-1 ) + particles[particleIdx] = particles[particleCount-1]; + particleCount--; + + if( particleCount == 0 && autoRemoveOnFinish_ ) { + [self unschedule:@selector(step:)]; + [[self parent] removeChild:self cleanup:YES]; + } + } + } + glBindBuffer(GL_ARRAY_BUFFER, verticesID); + glBufferData(GL_ARRAY_BUFFER, sizeof(ccPointSprite)*particleCount, vertices,GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +-(void) draw +{ +// int blendSrc, blendDst; +// int colorMode; + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture_.name); + + glEnable(GL_POINT_SPRITE_OES); + glTexEnvi( GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE ); + + glBindBuffer(GL_ARRAY_BUFFER, verticesID); + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2,GL_FLOAT,sizeof(vertices[0]),0); + + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(4, GL_FLOAT, sizeof(vertices[0]),(GLvoid*) offsetof(ccPointSprite,colors) ); + + glEnableClientState(GL_POINT_SIZE_ARRAY_OES); + glPointSizePointerOES(GL_FLOAT,sizeof(vertices[0]),(GLvoid*) offsetof(ccPointSprite,size) ); + + + BOOL newBlend = NO; + if( blendAdditive ) + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + else if( blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST ) { + newBlend = YES; + glBlendFunc( blendFunc_.src, blendFunc_.dst ); + } + + // save color mode +#if 0 + glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &colorMode); + if( colorModulate ) + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + else + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); +#endif + + glDrawArrays(GL_POINTS, 0, particleIdx); + + // restore blend state + if( blendAdditive || newBlend ) + glBlendFunc( CC_BLEND_SRC, CC_BLEND_DST); + +#if 0 + // restore color mode + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, colorMode); +#endif + + // unbind VBO buffer + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDisableClientState(GL_POINT_SIZE_ARRAY_OES); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glDisable(GL_TEXTURE_2D); + glDisable(GL_POINT_SPRITE_OES); +} + +#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 <= 64, @"PointParticleSystem doesn't support size > 64"); + [super setStartSize:size]; +} +@end + + diff --git a/cocos2d/QuadParticleSystem.h b/cocos2d/QuadParticleSystem.h new file mode 100644 index 0000000..cc0a233 --- /dev/null +++ b/cocos2d/QuadParticleSystem.h @@ -0,0 +1,44 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Leonardo Kasperavičius + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "ParticleSystem.h" + +/** QuadParticleSystem is a subclass of ParticleSystem + + 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 + - It is a bit slower that PointParticleSystem + - It consumes more RAM and more GPU memory than PointParticleSystem + @since v0.8 + */ +@interface QuadParticleSystem : ParticleSystem +{ + ccV2F_C4F_T2F_Quad *quads; // quads to be rendered + GLushort *indices; // indices + GLuint quadsID; // VBO id +} + + +// initialices the indices for the vertices +-(void) initIndices; +// initilizes the text coords +-(void) initTexCoords; + + +@end + diff --git a/cocos2d/QuadParticleSystem.m b/cocos2d/QuadParticleSystem.m new file mode 100644 index 0000000..b46939b --- /dev/null +++ b/cocos2d/QuadParticleSystem.m @@ -0,0 +1,319 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Leonardo Kasperavičius + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +// opengl +#import + +// cocos2d +#import "QuadParticleSystem.h" +#import "TextureMgr.h" +#import "ccMacros.h" + +// support +#import "Support/OpenGL_Internal.h" +#import "Support/CGPointExtension.h" + +@implementation QuadParticleSystem + +// overriding the init method +-(id) initWithTotalParticles:(int) numberOfParticles +{ + // base initialization + if( (self=[super initWithTotalParticles:numberOfParticles]) ) { + + // allocating data space + quads = malloc( sizeof(quads[0]) * totalParticles ); + indices = malloc( sizeof(indices[0]) * totalParticles * 6 ); + + if( !quads || !indices) { + NSLog(@"Particle system: not enough memory"); + if( quads ) + free( quads ); + if(indices) + free(indices); + return nil; + } + + // initialize only once the texCoords and the indices + [self initTexCoords]; + [self initIndices]; + + // 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); + } + + return self; +} + +-(void) dealloc +{ + free(quads); + free(indices); + glDeleteBuffers(1, &quadsID); + + [super dealloc]; +} + +-(void) initTexCoords +{ + for(int i=0; i rate ) { + [self addParticle]; + emitCounter -= rate; + } + + elapsed += dt; + if(duration != -1 && duration < elapsed) + [self stopSystem]; + } + + particleIdx = 0; + + // test performance with [self absolutePosition]; +// CGPoint absolutePosition = [self convertToWorldSpace:CGPointZero]; + CGPoint absolutePosition = position_; + + while( particleIdx < particleCount ) + { + Particle *p = &particles[particleIdx]; + + if( p->life > 0 ) { + + 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->radialAccel); + + // tangential acceleration + float newy = tangential.x; + tangential.x = -tangential.y; + tangential.y = newy; + tangential = ccpMult(tangential, p->tangentialAccel); + + // (gravity + radial + tangential) * dt + tmp = ccpAdd( ccpAdd( radial, tangential), gravity); + tmp = ccpMult( tmp, dt); + p->dir = ccpAdd( p->dir, tmp); + tmp = ccpMult(p->dir, dt); + p->pos = ccpAdd( p->pos, tmp ); + + 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); + + p->size += (p->deltaSize * dt); + p->size = MAX( 0, p->size ); + + p->life -= dt; + + // + // update values in quad + // + + CGPoint newPos = p->pos; + if( positionType_ == kPositionTypeFree ) { + newPos = ccpSub(absolutePosition, p->startPos); + newPos = ccpSub( p->pos, newPos); + } + + // colors + quads[particleIdx].bl.colors = p->color; + quads[particleIdx].br.colors = p->color; + quads[particleIdx].tl.colors = p->color; + quads[particleIdx].tr.colors = p->color; + + // vertices + float size_2 = p->size/2; + p->angle += (p->deltaAngle * dt); + if( p->angle ) { + float x1 = -size_2; + float y1 = -size_2; + + float x2 = x1 + p->size; + float y2 = y1 + p->size; + float x = newPos.x; + float y = newPos.y; + + float r = (float)-CC_DEGREES_TO_RADIANS(p->angle); + float cr = cosf(r); + float sr = sinf(r); + float ax = x1 * cr - y1 * sr + x; + float ay = x1 * sr + y1 * cr + y; + float bx = x2 * cr - y1 * sr + x; + float by = x2 * sr + y1 * cr + y; + float cx = x2 * cr - y2 * sr + x; + float cy = x2 * sr + y2 * cr + y; + float dx = x1 * cr - y2 * sr + x; + float dy = x1 * sr + y2 * cr + y; + + quads[particleIdx].bl.vertices.x = ax; + quads[particleIdx].bl.vertices.y = ay; + + // bottom-left vertex: + quads[particleIdx].br.vertices.x = bx; + quads[particleIdx].br.vertices.y = by; + + // top-right vertex: + quads[particleIdx].tl.vertices.x = dx; + quads[particleIdx].tl.vertices.y = dy; + + // top-right vertex: + quads[particleIdx].tr.vertices.x = cx; + quads[particleIdx].tr.vertices.y = cy; + } else { + // top-left vertex: + quads[particleIdx].bl.vertices.x = newPos.x - size_2; + quads[particleIdx].bl.vertices.y = newPos.y - size_2; + + // bottom-left vertex: + quads[particleIdx].br.vertices.x = newPos.x + size_2; + quads[particleIdx].br.vertices.y = newPos.y - size_2; + + // top-right vertex: + quads[particleIdx].tl.vertices.x = newPos.x - size_2; + quads[particleIdx].tl.vertices.y = newPos.y + size_2; + + // top-right vertex: + quads[particleIdx].tr.vertices.x = newPos.x + size_2; + quads[particleIdx].tr.vertices.y = newPos.y + size_2; + } + + // update particle counter + particleIdx++; + + } else { + // life < 0 + if( particleIdx != particleCount-1 ) + particles[particleIdx] = particles[particleCount-1]; + particleCount--; + + if( particleCount == 0 && autoRemoveOnFinish_ ) { + [self unschedule:@selector(step:)]; + [[self parent] removeChild:self cleanup:YES]; + } + } + } + glBindBuffer(GL_ARRAY_BUFFER, quadsID); + glBufferData(GL_ARRAY_BUFFER, sizeof(quads[0])*particleCount, quads,GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +// overriding draw method +-(void) draw +{ + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, texture_.name); + + glBindBuffer(GL_ARRAY_BUFFER, quadsID); + +#define kPointSize sizeof(quads[0].bl) + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2,GL_FLOAT, kPointSize, 0); + + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(4, GL_FLOAT, kPointSize, (GLvoid*) offsetof(ccV2F_C4F_T2F,colors) ); + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, kPointSize, (GLvoid*) offsetof(ccV2F_C4F_T2F,texCoords) ); + + + BOOL newBlend = NO; + if( blendAdditive ) + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + else if( blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST ) { + newBlend = YES; + glBlendFunc( blendFunc_.src, blendFunc_.dst ); + } + + // save color mode +#if 0 + glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &colorMode); + if( colorModulate ) + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + else + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); +#endif + + glDrawElements(GL_TRIANGLES, particleIdx*6, GL_UNSIGNED_SHORT, indices); + + // restore blend state + if( blendAdditive || newBlend ) + glBlendFunc( CC_BLEND_SRC, CC_BLEND_DST ); + +#if 0 + // restore color mode + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, colorMode); +#endif + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glDisable(GL_TEXTURE_2D); +} + +@end + + diff --git a/cocos2d/RenderTexture.h b/cocos2d/RenderTexture.h new file mode 100644 index 0000000..4ad7afc --- /dev/null +++ b/cocos2d/RenderTexture.h @@ -0,0 +1,64 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Jason Booth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import +#import "CocosNode.h" +#import "Sprite.h" +#import "OpenGL_Internal.h" + +enum +{ + kImageFormatJPG = 0, + kImageFormatPNG = 1 +}; + + +/** + RenderTexture 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 RenderTexture : CocosNode +{ + GLuint fbo; + GLint oldFBO; + Texture2D* texture; + Sprite* sprite; +} + +/** sprite being used */ +@property (nonatomic,readwrite, assign) Sprite* sprite; + +/** creates a RenderTexture object with width and height */ ++(id)renderTextureWithWidth:(int)width height:(int)height; +/** initializes a RenderTexture object with width and height */ +-(id)initWithWidth:(int)width height:(int)height; +-(void)begin; +-(void)end; +/* get buffer as UIImage */ +-(UIImage *)getUIImageFromBuffer; +/** 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; +/** clears the texture with a color */ +-(void)clear:(float)r g:(float)g b:(float)b a:(float)a; +@end + + diff --git a/cocos2d/RenderTexture.m b/cocos2d/RenderTexture.m new file mode 100644 index 0000000..099cf3f --- /dev/null +++ b/cocos2d/RenderTexture.m @@ -0,0 +1,189 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Jason Booth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "RenderTexture.h" +#import "cocos2d.h" +#include "glu.h" + +@implementation RenderTexture + +@synthesize sprite; + ++(id)renderTextureWithWidth:(int)w height:(int)h +{ + self = [[[RenderTexture alloc] initWithWidth:w height:h] autorelease]; + return self; +} + +-(id)initWithWidth:(int)w height:(int)h +{ + self = [super init]; + if (self) + { + glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &oldFBO); + Texture2DPixelFormat format = kTexture2DPixelFormat_RGBA8888; + // textures must be power of two squared + int pow = 8; + while (pow < w || pow < h) pow*=2; + + void *data = malloc((int)(pow * pow * 4)); + memset(data, 0, (int)(pow * pow * 4)); + texture = [[[Texture2D alloc] initWithData:data pixelFormat:format pixelsWide:pow pixelsHigh:pow contentSize:CGSizeMake(w, h)] autorelease]; + free( data ); + + // generate FBO + glGenFramebuffersOES(1, &fbo); + glBindFramebufferOES(GL_FRAMEBUFFER_OES, fbo); + + // associate texture with FBO + glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, texture.name, 0); + + // check if it worked (probably worth doing :) ) + GLuint status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES); + if (status != GL_FRAMEBUFFER_COMPLETE_OES) + { + [NSException raise:@"Render Texture" format:@"Could not attach texture to framebuffer"]; + } + sprite = [Sprite spriteWithTexture:texture]; + [sprite setScaleY:-1]; + [self addChild:sprite]; + glBindFramebufferOES(GL_FRAMEBUFFER_OES, oldFBO); + } + return self; +} + +-(void)dealloc +{ + [self removeAllChildrenWithCleanup:YES]; + glDeleteFramebuffersOES(1, &fbo); + [super dealloc]; +} + +-(void)begin +{ + glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &oldFBO); + glBindFramebufferOES(GL_FRAMEBUFFER_OES, fbo);//Will direct drawing to the frame buffer created above + glDisable(GL_DITHER); +} + +-(void)clear:(float)r g:(float)g b:(float)b a:(float)a +{ + [self begin]; + glColorMask(TRUE, TRUE, TRUE, TRUE); + glClearColor(r, g, b, a); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glColorMask(TRUE, TRUE, TRUE, FALSE); + [self end]; +} + +-(void)end +{ + glBindFramebufferOES(GL_FRAMEBUFFER_OES, oldFBO); +} + +-(BOOL)saveBuffer:(NSString*)name +{ + return [self saveBuffer:name format:kImageFormatJPG]; +} + +-(BOOL)saveBuffer:(NSString*)fileName format:(int)format +{ + UIImage *myImage = [self getUIImageFromBuffer]; + + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:fileName]; + + NSData *data; + + if (format == kImageFormatPNG) + data = UIImagePNGRepresentation(myImage); + else + data = UIImageJPEGRepresentation(myImage, 1.0f); + + return [data writeToFile:fullPath atomically:YES]; +} + +/* get buffer as UIImage */ +-(UIImage *)getUIImageFromBuffer +{ + int tx = texture.contentSize.width; + int ty = texture.contentSize.height; + + int bitsPerComponent = 8; + int bitsPerPixel = 32; + int bytesPerPixel = (bitsPerComponent * 4)/8; + int bytesPerRow = bytesPerPixel * tx; + NSInteger myDataLength = bytesPerRow * ty; + + unsigned char buffer[myDataLength]; + + [self begin]; + glReadPixels(0,0,tx,ty,GL_RGBA,GL_UNSIGNED_BYTE, &buffer); + [self end]; + /* + CGImageCreate(size_t width, size_t height, + size_t bitsPerComponent, size_t bitsPerPixel, size_t bytesPerRow, + CGColorSpaceRef space, CGBitmapInfo bitmapInfo, CGDataProviderRef provider, + const CGFloat decode[], bool shouldInterpolate, + CGColorRenderingIntent intent) + */ + // make data provider with data. + + CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault; + CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, myDataLength, NULL); + CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); + CGImageRef iref = CGImageCreate(tx, ty, + bitsPerComponent, bitsPerPixel, bytesPerRow, + colorSpaceRef, bitmapInfo, provider, + NULL, false, + kCGRenderingIntentDefault); + /* Create a bitmap context. The context draws into a bitmap which is `width' + pixels wide and `height' pixels high. The number of components for each + pixel is specified by `colorspace', which may also specify a destination + color profile. The number of bits for each component of a pixel is + specified by `bitsPerComponent'. The number of bytes per pixel is equal + to `(bitsPerComponent * number of components + 7)/8'. Each row of the + bitmap consists of `bytesPerRow' bytes, which must be at least `width * + bytes per pixel' bytes; in addition, `bytesPerRow' must be an integer + multiple of the number of bytes per pixel. `data' points a block of + memory at least `bytesPerRow * height' bytes. `bitmapInfo' specifies + whether the bitmap should contain an alpha channel and how it's to be + generated, along with whether the components are floating-point or + integer. + + CGContextRef CGBitmapContextCreate(void *data, size_t width, + size_t height, size_t bitsPerComponent, size_t bytesPerRow, + CGColorSpaceRef colorspace, CGBitmapInfo bitmapInfo) + */ + uint32_t* pixels = (uint32_t *)malloc(myDataLength); + CGContextRef context = CGBitmapContextCreate(pixels, 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]; + + free(pixels); + CGImageRelease(iref); + CGContextRelease(context); + CGColorSpaceRelease(colorSpaceRef); + CGDataProviderRelease(provider); + CGImageRelease(outputRef); + + return [image autorelease]; +} +@end diff --git a/cocos2d/Ribbon.h b/cocos2d/Ribbon.h new file mode 100644 index 0000000..9184d29 --- /dev/null +++ b/cocos2d/Ribbon.h @@ -0,0 +1,105 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008, 2009 Jason Booth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + * + */ + +#import "CocosNode.h" +#import "Texture2D.h" +#import + +/** + * 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. + * + * @since v0.8.1 + */ +@interface Ribbon : CocosNode +{ + NSMutableArray* mSegments; + NSMutableArray* dSegments; + + CGPoint mLastPoint1; + CGPoint mLastPoint2; + CGPoint mLastLocation; + int mVertCount; + float mTexVPos; + float mCurTime; + float mFadeTime; + float mDelta; + float mLastWidth; + float mLastSign; + BOOL mPastFirstPoint; + + // Texture used + Texture2D* texture_; + + // texture lenght + float textureLength_; + + // RGBA protocol + ccColor4B color_; + + // blend func + ccBlendFunc blendFunc_; +} + +/** Texture used by the ribbon. Conforms to CocosNodeTexture protocol */ +@property (nonatomic,readwrite,retain) Texture2D* texture; + +/** Texture lenghts 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 RibbonSegment : 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/cocos2d/Ribbon.m b/cocos2d/Ribbon.m new file mode 100644 index 0000000..9c463ff --- /dev/null +++ b/cocos2d/Ribbon.m @@ -0,0 +1,365 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008, 2009 Jason Booth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + * + * + * 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 "Ribbon.h" +#import "TextureMgr.h" +#import "Support/CGPointExtension.h" +#import "ccMacros.h" + +// +// Ribbon +// +@implementation Ribbon +@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 = [[[Ribbon 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) + { + + mSegments = [[NSMutableArray alloc] init]; + dSegments = [[NSMutableArray alloc] init]; + + /* 1 initial segment */ + RibbonSegment* seg = [[[RibbonSegment alloc] init] autorelease]; + [mSegments addObject:seg]; + + textureLength_ = l; + + color_ = color; + mFadeTime = fade; + mLastLocation = CGPointZero; + mLastWidth = w/2; + mTexVPos = 0.0f; + + mCurTime = 0; + mPastFirstPoint = 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 = [[TextureMgr sharedTextureMgr] addImage:path]; + + /* default texture parameter */ + ccTexParams params = { GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT }; + [texture_ setTexParameters:¶ms]; + } + return self; +} + +-(void)dealloc +{ + [mSegments release]; + [dSegments 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 +{ + mCurTime+= delta; + mDelta = 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 +{ + w=w*0.5f; + // if this is the first point added, cache it and return + if (!mPastFirstPoint) + { + mLastWidth = w; + mLastLocation = location; + mPastFirstPoint = YES; + return; + } + + CGPoint sub = ccpSub(mLastLocation, 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(mLastLocation.x - location.x, 2) + powf(mLastLocation.y - location.y, 2)); + float tend = mTexVPos + len/textureLength_; + RibbonSegment* seg; + // grab last segment + seg = [mSegments objectAtIndex:[mSegments count]-1]; + // lets kill old segments + for (RibbonSegment* seg2 in mSegments) + { + if (seg2 != seg && seg2->finished) + { + [dSegments addObject:seg2]; + } + } + [mSegments removeObjectsInArray:dSegments]; + // is the segment full? + if (seg->end >= 50) + [mSegments removeObjectsInArray:dSegments]; + // grab last segment and appent to it if it's not full + seg = [mSegments objectAtIndex:[mSegments count]-1]; + // is the segment full? + if (seg->end >= 50) + { + RibbonSegment* newSeg; + // grab it from the cache if we can + if ([dSegments count] > 0) + { + newSeg = [dSegments objectAtIndex:0]; + [dSegments removeObject:newSeg]; + [newSeg reset]; + } + else + { + newSeg = [[[RibbonSegment alloc] init] autorelease]; + } + + 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; + [mSegments addObject:seg]; + } + if (seg->end == 0) + { + // first edge has to get rotation from the first real polygon + CGPoint lp1 = ccpAdd([self rotatePoint:ccp(-mLastWidth, 0) rotation:r], mLastLocation); + CGPoint lp2 = ccpAdd([self rotatePoint:ccp(+mLastWidth, 0) rotation:r], mLastLocation); + seg->creationTime[0] = mCurTime - mDelta; + 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] = mTexVPos; + seg->coords[2] = 1.0f; + seg->coords[3] = mTexVPos; + seg->end++; + } + + int v = seg->end*6; + int c = seg->end*4; + // add new vertex + seg->creationTime[seg->end] = mCurTime; + 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; + + mTexVPos = tend; + mLastLocation = location; + mLastPoint1 = p1; + mLastPoint2 = p2; + mLastWidth = w; + seg->end++; +} + +-(void) draw +{ + if ([mSegments count] > 0) + { + glEnableClientState( GL_VERTEX_ARRAY); + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + glEnable( GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, [texture_ name]); + + BOOL newBlend = NO; + if( blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST ) { + newBlend = YES; + glBlendFunc( blendFunc_.src, blendFunc_.dst ); + } + + for (RibbonSegment* seg in mSegments) + [seg draw:mCurTime fadeTime:mFadeTime color:color_]; + + if( newBlend ) + glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + + glDisable( GL_TEXTURE_2D); + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + glDisableClientState( GL_COLOR_ARRAY ); + } +} + +#pragma mark Ribbon - CocosNodeTexture protocol +-(void) setTexture:(Texture2D*) texture +{ + [texture_ release]; + texture_ = [texture retain]; + [self setContentSize: texture.contentSize]; + /* XXX Don't update blending function in Ribbons */ +} + +-(Texture2D*) texture +{ + return texture_; +} + +@end + + +#pragma mark - +#pragma mark RibbonSegment + +@implementation RibbonSegment + +-(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 +{ + CCLOG(@"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 mCurTime != 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/cocos2d/Scene.h b/cocos2d/Scene.h new file mode 100644 index 0000000..17c60f3 --- /dev/null +++ b/cocos2d/Scene.h @@ -0,0 +1,33 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import + +#import "CocosNode.h" + +/** Scene is a subclass of CocosNode that is used only as an abstract concept. + + Scene an CocosNode are almost identical with the difference that Scene has it's + anchor point (by default) at the center of the screen. + + For the moment Scene has no other logic than that, but in future releases it might have + additional logic. + + It is a good practice to use and Scene as the parent of all your nodes. +*/ +@interface Scene : CocosNode { + +} +@end diff --git a/cocos2d/Scene.m b/cocos2d/Scene.m new file mode 100644 index 0000000..dbfb3f3 --- /dev/null +++ b/cocos2d/Scene.m @@ -0,0 +1,32 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import "Scene.h" +#import "Director.h" +#import "Support/CGPointExtension.h" + +@implementation Scene +-(id) init +{ + if( (self=[super init]) ) { + CGSize s = [[Director sharedDirector] winSize]; + self.relativeAnchorPoint = NO; + anchorPoint_ = ccp(0.5f, 0.5f); + [self setContentSize:s]; + } + + return self; +} +@end diff --git a/cocos2d/Scheduler.h b/cocos2d/Scheduler.h new file mode 100644 index 0000000..55035b0 --- /dev/null +++ b/cocos2d/Scheduler.h @@ -0,0 +1,106 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +// cocoa related +#import + +#import "ccTypes.h" + +typedef void (*TICK_IMP)(id, SEL, ccTime); + +// +// Timer +// +/** Light weight timer */ +@interface Timer : NSObject +{ + id target; + SEL selector; + TICK_IMP impMethod; + + ccTime interval; + ccTime elapsed; +} + +/** 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) fire: (ccTime) dt; +@end + +// +// Scheduler +// +/** Scheduler is responsible of triggering the scheduled callbacks. + You should not use NSTimer. Instead use this class. +*/ +@interface Scheduler : NSObject +{ + NSMutableArray *scheduledMethods; + NSMutableArray *methodsToRemove; + NSMutableArray *methodsToAdd; + + ccTime timeScale_; +} + +/** 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 */ ++(Scheduler *)sharedScheduler; + +/** 'tick' the scheduler. + You should NEVER call this method, unless you know what you are doing. + */ +-(void) tick:(ccTime)dt; + +/** schedules a Timer. + It will be fired in every frame. + */ +-(void) scheduleTimer: (Timer*) t; + +/** unschedules an already scheduled Timer */ +-(void) unscheduleTimer: (Timer*) t; + +/** unschedule all timers. + You should NEVER call this method, unless you know what you are doing. + @since v0.8 + */ +-(void) unscheduleAllTimers; +@end diff --git a/cocos2d/Scheduler.m b/cocos2d/Scheduler.m new file mode 100644 index 0000000..5809886 --- /dev/null +++ b/cocos2d/Scheduler.m @@ -0,0 +1,217 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +// cocos2d imports +#import "Scheduler.h" +#import "ccMacros.h" + +// +// Timer +// +@implementation Timer + +@synthesize interval; + +-(id) init +{ + NSException* myException = [NSException + exceptionWithName:@"TimerInvalid" + reason:@"Invalid init for Timer. Use initWithTarget:sel:" + userInfo:nil]; + @throw myException; +} + ++(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]) ) { +#ifdef 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 being retained. Be careful with ciruclar references + target = [t retain]; + 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 +{ + CCLOG( @"deallocing %@", self); + [target release]; + [super dealloc]; +} + +-(void) fire: (ccTime) dt +{ + if( elapsed == - 1) + elapsed = 0; + else + elapsed += dt; + if( elapsed >= interval ) { + impMethod(target, selector, elapsed); + elapsed = 0; + } +} +@end + +// +// Scheduler +// +@implementation Scheduler + +static Scheduler *sharedScheduler; + +@synthesize timeScale = timeScale_; + ++ (Scheduler *)sharedScheduler +{ + @synchronized([Scheduler class]) + { + if (!sharedScheduler) + [[Scheduler alloc] init]; + + return sharedScheduler; + } + // to avoid compiler warning + return nil; +} + ++(id)alloc +{ + @synchronized([Scheduler class]) + { + NSAssert(sharedScheduler == nil, @"Attempted to allocate a second instance of a singleton."); + sharedScheduler = [super alloc]; + return sharedScheduler; + } + // to avoid compiler warning + return nil; +} + +- (id) init +{ + if( (self=[super init]) ) { + scheduledMethods = [[NSMutableArray arrayWithCapacity:50] retain]; + methodsToRemove = [[NSMutableArray arrayWithCapacity:20] retain]; + methodsToAdd = [[NSMutableArray arrayWithCapacity:20] retain]; + + timeScale_ = 1.0f; + } + + return self; +} + +- (void) dealloc +{ + CCLOG( @"deallocing %@", self); + [scheduledMethods release]; + [methodsToRemove release]; + [methodsToAdd release]; + sharedScheduler = nil; + + [super dealloc]; +} + +-(void) scheduleTimer: (Timer*) t +{ + // it is possible that sometimes (in transitions in particular) an scene unschedule a timer + // and before the timer is deleted, it is re-scheduled + if( [methodsToRemove containsObject:t] ) + { + [methodsToRemove removeObject:t]; + return; + } + + if( [scheduledMethods containsObject:t] || [methodsToAdd containsObject:t]) { + NSLog(@"Scheduler.schedulerTimer: timer %@ already scheduled", t); + NSException* myException = [NSException + exceptionWithName:@"SchedulerTimerAlreadyScheduled" + reason:@"Scheduler.scheduleTimer already scheduled" + userInfo:nil]; + @throw myException; + } + + [methodsToAdd addObject: t]; +} + +-(void) unscheduleTimer: (Timer*) t; +{ + // someone wants to remove it before it was added + if( [methodsToAdd containsObject:t] ) { + [methodsToAdd removeObject:t]; + return; + } + + if( ![scheduledMethods containsObject:t] ) { + NSLog(@"Scheduler.unscheduleTimer: timer not scheduled"); + NSException* myException = [NSException + exceptionWithName:@"SchedulerTimerNotFound" + reason:@"Scheduler.unscheduleTimer not found" + userInfo:nil]; + @throw myException; + } + + [methodsToRemove addObject:t]; +} + +-(void) unscheduleAllTimers +{ + [methodsToAdd removeAllObjects]; + [methodsToRemove removeAllObjects]; + [scheduledMethods removeAllObjects]; +} + +-(void) tick: (ccTime) dt +{ + if( timeScale_ != 1.0f ) + dt *= timeScale_; + + for( id k in methodsToRemove ) + [scheduledMethods removeObject:k]; + + [methodsToRemove removeAllObjects]; + + for( id k in methodsToAdd ) + [scheduledMethods addObject:k]; + [methodsToAdd removeAllObjects]; + + for( Timer *t in scheduledMethods ) + [t fire: dt]; +} +@end diff --git a/cocos2d/Sprite.h b/cocos2d/Sprite.h new file mode 100644 index 0000000..61033a3 --- /dev/null +++ b/cocos2d/Sprite.h @@ -0,0 +1,100 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import + +#import "Support/Texture2D.h" + +#import "TextureNode.h" + +#pragma mark Sprite + +@class Animation; +/** Sprite is a subclass of TextureNode that implements the CocosNodeFrames protocol. + * + * Sprite supports ALL CocosNode transformations, but in contrast to AtlasSprite it is much slower. + * ONLY use Sprite if you can't achieve the same with effect with AtlasSprite, otherwise the use of + * AtlasSprite is recommended. + * + * All features from TextureNode are valid, plus the following new features: + * - it supports animations (frames) + * + * Limitations of Sprite: + * - It doesn't perform as well as AtlasSprite + */ +@interface Sprite : TextureNode +{ + NSMutableDictionary *animations; +} + +/** creates an sprite with an image file. The file will be loaded using the TextureMgr */ ++ (id) spriteWithFile:(NSString *)imageFile; +/** creates an sprite from a CGImageRef image */ ++ (id) spriteWithCGImage:(CGImageRef)image; + +/** initializes the sprite with an image file. The file will be loaded using the TextureMg */ +- (id) initWithFile:(NSString *) imageFile; +/** creates an sprite from a CGImageRef image */ +- (id) initWithCGImage:(CGImageRef)image; +/** creates an sprite with a Texture2D instance */ ++(id) spriteWithTexture:(Texture2D*) tex; +/** initializes the sprite with a Texture2D instance */ +-(id) initWithTexture:(Texture2D*) tex; +@end + +#pragma mark Animation + +/** an Animation object used within Sprites to perform animations */ +@interface Animation : NSObject +{ + NSString *name; + float delay; + NSMutableArray *frames; +} + +@property (nonatomic,readwrite,copy) NSString * name; + +// CocosAnimation +@property (nonatomic,readwrite,assign) float delay; +@property (nonatomic,readwrite,retain) NSMutableArray *frames; + +/** creates an Animation with name, delay and frames from image files */ ++(id) animationWithName: (NSString*) name delay:(float)delay; + +/** creates an Animation with name, delay and frames from image files */ ++(id) animationWithName: (NSString*) name delay:(float)delay images:image1,... NS_REQUIRES_NIL_TERMINATION; + +/** creates an Animation with name, delay and frames from Texture2D objects */ ++(id) animationWithName: (NSString*) name delay:(float)delay textures:tex1,... NS_REQUIRES_NIL_TERMINATION; + +/** initializes an Animation with name, delay and frames from Texture2D objects */ +-(id) initWithName: (NSString*) name delay:(float)delay firstTexture:(Texture2D*)tex vaList:(va_list) args; + + + +/** initializes an Animation with name and delay + */ +-(id) initWithName: (NSString*) name delay:(float)delay; + +/** initializes an Animation with name, delay and frames from image files + */ +-(id) initWithName: (NSString*) name delay:(float)delay firstImage:(NSString*)filename vaList:(va_list) args; + +/** adds a frame to an Animation */ +-(void) addFrameWithFilename: (NSString*) filename; + +/** adds a frame from a Texture2D object to an Animation */ +-(void) addFrameWithTexture: (Texture2D*) tex; +@end diff --git a/cocos2d/Sprite.m b/cocos2d/Sprite.m new file mode 100644 index 0000000..f94bf51 --- /dev/null +++ b/cocos2d/Sprite.m @@ -0,0 +1,253 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "TextureMgr.h" +#import "Sprite.h" +#import "ccMacros.h" +#import "Support/CGPointExtension.h" + +#pragma mark Sprite + +@interface Sprite (Private) +// lazy allocation +-(void) initAnimationDictionary; +@end + +@implementation Sprite + +#pragma mark Sprite - image file ++ (id) spriteWithFile:(NSString*) filename +{ + return [[[self alloc] initWithFile:filename] autorelease]; +} + +- (id) initWithFile:(NSString*) filename +{ + self = [super init]; + if( self ) { + // texture is retained + self.texture = [[TextureMgr sharedTextureMgr] addImage: filename]; + + // lazy alloc + animations = nil; + } + + return self; +} + +#pragma mark Sprite - CGImageRef + ++ (id) spriteWithCGImage: (CGImageRef) image +{ + return [[[self alloc] initWithCGImage:image] autorelease]; +} + +- (id) initWithCGImage: (CGImageRef) image +{ + self = [super init]; + if( self ) { + // XXX: possible bug. See issue #349. New API should be added + NSString *key = [NSString stringWithFormat:@"%08X",(unsigned long)image]; + self.texture = [[TextureMgr sharedTextureMgr] addCGImage:image forKey:key]; + + + // lazy alloc + animations = nil; + } + + return self; +} + +#pragma mark Sprite - Texture2D + ++ (id) spriteWithTexture:(Texture2D*) tex +{ + return [[[self alloc] initWithTexture:tex] autorelease]; +} + +- (id) initWithTexture:(Texture2D*) tex +{ + if( (self = [super init]) ) { + // texture is retained + self.texture = tex; + + // lazy alloc + animations = nil; + } + return self; +} + +#pragma mark Sprite + +-(void) dealloc +{ + [animations release]; + [super dealloc]; +} + +-(void) initAnimationDictionary +{ + animations = [[NSMutableDictionary dictionaryWithCapacity:2] retain]; +} + +// +// CocosNodeFrames protocol +// +-(void) setDisplayFrame:(id)frame +{ + self.texture = frame; +} + +-(void) setDisplayFrame: (NSString*) animationName index:(int) frameIndex +{ + if( ! animations ) + [self initAnimationDictionary]; + + Animation *a = [animations objectForKey: animationName]; + Texture2D *frame = [[a frames] objectAtIndex:frameIndex]; + self.texture = frame; +} + +-(BOOL) isFrameDisplayed:(id)frame +{ + return texture_ == frame; +} +-(id) displayFrame +{ + return texture_; +} +-(void) addAnimation: (id) anim +{ + // lazy alloc + if( ! animations ) + [self initAnimationDictionary]; + + [animations setObject:anim forKey:[anim name]]; +} +-(id)animationByName: (NSString*) animationName +{ + NSAssert( animationName != nil, @"animationName parameter must be non nil"); + return [animations objectForKey:animationName]; +} +@end + +#pragma mark - +#pragma mark Animation + +@implementation Animation +@synthesize name, delay, frames; + ++(id) animationWithName:(NSString*)n delay:(float)d +{ + return [[[self alloc] initWithName:n delay:d] autorelease]; +} + +-(id) initWithName: (NSString*) n delay:(float)d +{ + return [self initWithName:n delay:d firstImage:nil vaList:nil]; +} + +-(void) dealloc +{ + CCLOG( @"deallocing %@",self); + [name release]; + [frames release]; + [super dealloc]; +} + +#pragma mark Animation - image files + ++(id) animationWithName: (NSString*) name delay:(float)delay images:image1,... +{ + va_list args; + va_start(args,image1); + + id s = [[[self alloc] initWithName:name delay:delay firstImage:image1 vaList:args] autorelease]; + + va_end(args); + return s; +} + +-(id) initWithName: (NSString*) n delay:(float)d firstImage:(NSString*)image vaList: (va_list) args +{ + if( ! (self=[super init]) ) + return nil; + + name = [n retain]; + frames = [[NSMutableArray array] retain]; + delay = d; + + if( image ) { + Texture2D *tex = [[TextureMgr sharedTextureMgr] addImage: image]; + [frames addObject:tex]; + + NSString *filename = va_arg(args, NSString*); + while(filename) { + tex = [[TextureMgr sharedTextureMgr] addImage: filename]; + [frames addObject:tex]; + + filename = va_arg(args, NSString*); + } + } + return self; +} + +-(void) addFrameWithFilename: (NSString*) filename +{ + Texture2D *tex = [[TextureMgr sharedTextureMgr] addImage: filename]; + [frames addObject:tex]; +} + +#pragma mark Animation - Texture2D + ++(id) animationWithName: (NSString*) name delay:(float)delay textures:tex1,... +{ + va_list args; + va_start(args,tex1); + + id s = [[[self alloc] initWithName:name delay:delay firstTexture:tex1 vaList:args] autorelease]; + + va_end(args); + return s; +} + +-(id) initWithName:(NSString*)n delay:(float)d firstTexture:(Texture2D*)tex vaList:(va_list)args +{ + self = [super init]; + if( self ) { + name = [n retain]; + frames = [[NSMutableArray array] retain]; + delay = d; + + if( tex ) { + [frames addObject:tex]; + + Texture2D *newTex = va_arg(args, Texture2D*); + while(newTex) { + [frames addObject:newTex]; + + newTex = va_arg(args, Texture2D*); + } + } + } + return self; +} + +-(void) addFrameWithTexture: (Texture2D*) tex +{ + [frames addObject:tex]; +} + +@end + diff --git a/cocos2d/Support/CGPointExtension.h b/cocos2d/Support/CGPointExtension.h new file mode 100644 index 0000000..297fe10 --- /dev/null +++ b/cocos2d/Support/CGPointExtension.h @@ -0,0 +1,222 @@ +/* cocos2d for iPhone + * http://www.cocos2d-iphone.org + * + * 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. + */ + +/* + * Code based on Chipmunk's cpVect.h file + */ + +/** + @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 +#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); + +#ifdef __cplusplus +} +#endif diff --git a/cocos2d/Support/CGPointExtension.m b/cocos2d/Support/CGPointExtension.m new file mode 100644 index 0000000..d1ebcc5 --- /dev/null +++ b/cocos2d/Support/CGPointExtension.m @@ -0,0 +1,55 @@ +/* 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. + */ + +#include "stdio.h" +#include "math.h" + +#include "CGPointExtension.h" + +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); +} diff --git a/cocos2d/Support/EAGLView.h b/cocos2d/Support/EAGLView.h new file mode 100755 index 0000000..97236d6 --- /dev/null +++ b/cocos2d/Support/EAGLView.h @@ -0,0 +1,145 @@ +/* + +===== 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. + +*/ + +#import +#import +#import +#import +#import + +//CLASSES: + +@class EAGLView; + +//PROTOCOLS: + +@protocol EAGLViewDelegate +- (void) didResizeEAGLSurfaceForView:(EAGLView*)view; //Called whenever the EAGL surface has been resized +@end + +@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 +{ +@private + NSString* _format; + GLuint _depthFormat; + BOOL _autoresize; + EAGLContext *_context; + GLuint _framebuffer; + GLuint _renderbuffer; + GLuint _depthBuffer; + CGSize _size; + BOOL _hasBeenCurrent; + id _delegate; + id touchDelegate; +} +- (id) initWithFrame:(CGRect)frame; //These also set the current context +- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format; +- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained; + +/** frame buffer id */ +@property(nonatomic,readonly) GLuint framebuffer; +/** pixel format */ +@property(nonatomic,readonly) NSString* pixelFormat; +/** depth format */ +@property(nonatomic,readonly) GLuint depthFormat; +/** EAGL context */ +@property(nonatomic,readonly) EAGLContext *context; + +/** whether or not the surface will auto resize. + NO by default - Set to YES to have the EAGL surface automatically resized when the view bounds change, + otherwise the EAGL surface contents is rendered scaled + */ +@property (nonatomic,readwrite) BOOL autoresizesSurface; +/** surface size */ +@property(nonatomic,readonly, nonatomic) CGSize surfaceSize; + +/** delegate */ +@property(nonatomic,readwrite,assign) id delegate; +/** touch delegate */ +@property(nonatomic,readwrite,assign) id touchDelegate; + +- (void) setAutoresizesEAGLSurface:(BOOL)autoresizesEAGLSurface; + +- (void) setCurrentContext; +- (BOOL) isCurrentContext; +- (void) clearCurrentContext; + +- (void) swapBuffers; //This also checks the current OpenGL error and logs an error if needed + +- (CGPoint) convertPointFromViewToSurface:(CGPoint)point; +- (CGRect) convertRectFromViewToSurface:(CGRect)rect; +@end diff --git a/cocos2d/Support/EAGLView.m b/cocos2d/Support/EAGLView.m new file mode 100755 index 0000000..25db666 --- /dev/null +++ b/cocos2d/Support/EAGLView.m @@ -0,0 +1,316 @@ +/* + +===== 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. + +*/ + +#import + +#import "EAGLView.h" +#import "OpenGL_Internal.h" + +//CLASS IMPLEMENTATIONS: + +@implementation EAGLView + +@synthesize delegate=_delegate, autoresizesSurface=_autoresize, surfaceSize=_size, framebuffer = _framebuffer, pixelFormat = _format, depthFormat = _depthFormat, context = _context, touchDelegate; + ++ (Class) layerClass +{ + return [CAEAGLLayer class]; +} + +- (BOOL) _createSurface +{ + CAEAGLLayer* eaglLayer = (CAEAGLLayer*)[self layer]; + CGSize newSize; + GLuint oldRenderbuffer; + GLuint oldFramebuffer; + + if(![EAGLContext setCurrentContext:_context]) { + return NO; + } + + newSize = [eaglLayer bounds].size; + newSize.width = roundf(newSize.width); + newSize.height = roundf(newSize.height); + + glGetIntegerv(GL_RENDERBUFFER_BINDING_OES, (GLint *) &oldRenderbuffer); + glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, (GLint *) &oldFramebuffer); + + glGenRenderbuffersOES(1, &_renderbuffer); + glBindRenderbufferOES(GL_RENDERBUFFER_OES, _renderbuffer); + + if(![_context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:eaglLayer]) { + glDeleteRenderbuffersOES(1, &_renderbuffer); + glBindRenderbufferOES(GL_RENDERBUFFER_BINDING_OES, oldRenderbuffer); + return NO; + } + + glGenFramebuffersOES(1, &_framebuffer); + glBindFramebufferOES(GL_FRAMEBUFFER_OES, _framebuffer); + glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, _renderbuffer); + if (_depthFormat) { + glGenRenderbuffersOES(1, &_depthBuffer); + glBindRenderbufferOES(GL_RENDERBUFFER_OES, _depthBuffer); + glRenderbufferStorageOES(GL_RENDERBUFFER_OES, _depthFormat, newSize.width, newSize.height); + glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, _depthBuffer); + } + + _size = newSize; + if(!_hasBeenCurrent) { + glViewport(0, 0, newSize.width, newSize.height); + glScissor(0, 0, newSize.width, newSize.height); + _hasBeenCurrent = YES; + } + else { + glBindFramebufferOES(GL_FRAMEBUFFER_OES, oldFramebuffer); + } + glBindRenderbufferOES(GL_RENDERBUFFER_OES, oldRenderbuffer); + + + CHECK_GL_ERROR(); + + [_delegate didResizeEAGLSurfaceForView:self]; + + return YES; +} + +- (void) _destroySurface +{ + EAGLContext *oldContext = [EAGLContext currentContext]; + + if (oldContext != _context) + [EAGLContext setCurrentContext:_context]; + + if(_depthFormat) { + glDeleteRenderbuffersOES(1, &_depthBuffer); + _depthBuffer = 0; + } + + glDeleteRenderbuffersOES(1, &_renderbuffer); + _renderbuffer = 0; + + glDeleteFramebuffersOES(1, &_framebuffer); + _framebuffer = 0; + + if (oldContext != _context) + [EAGLContext setCurrentContext:oldContext]; + else + [EAGLContext setCurrentContext:nil]; +} + +- (id) initWithFrame:(CGRect)frame +{ + return [self initWithFrame:frame pixelFormat:kEAGLColorFormatRGB565 depthFormat:0 preserveBackbuffer:NO]; +} + +- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format +{ + return [self initWithFrame:frame pixelFormat:format depthFormat:0 preserveBackbuffer:NO]; +} + +- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained +{ + if((self = [super initWithFrame:frame])) + { + + [self setOpaque:YES]; + + CAEAGLLayer* eaglLayer = (CAEAGLLayer*)[self layer]; + + [eaglLayer setDrawableProperties:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:retained], kEAGLDrawablePropertyRetainedBacking, format, kEAGLDrawablePropertyColorFormat, nil]]; + _format = format; + _depthFormat = depth; + + _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; + if(_context == nil) { + [self release]; + return nil; + } + + if(![self _createSurface]) { + [self release]; + return nil; + } + } + + return self; +} + +- (void) dealloc +{ + [self _destroySurface]; + + [_context release]; + _context = nil; + + [super dealloc]; +} + +- (void) layoutSubviews +{ + CGRect bounds = [self bounds]; + + if(_autoresize && ((roundf(bounds.size.width) != _size.width) || (roundf(bounds.size.height) != _size.height))) { + [self _destroySurface]; +#if __DEBUG__ + REPORT_ERROR(@"Resizing surface from %fx%f to %fx%f", _size.width, _size.height, roundf(bounds.size.width), roundf(bounds.size.height)); +#endif + [self _createSurface]; + } +} + +- (void) setAutoresizesEAGLSurface:(BOOL)autoresizesEAGLSurface; +{ + _autoresize = autoresizesEAGLSurface; + if(_autoresize) + [self layoutSubviews]; +} + +- (void) setCurrentContext +{ + if(![EAGLContext setCurrentContext:_context]) { + printf("Failed to set current context %p in %s\n", _context, __FUNCTION__); + } +} + +- (BOOL) isCurrentContext +{ + return ([EAGLContext currentContext] == _context ? YES : NO); +} + +- (void) clearCurrentContext +{ + if(![EAGLContext setCurrentContext:nil]) + printf("Failed to clear current context in %s\n", __FUNCTION__); +} + +- (void) swapBuffers +{ + EAGLContext *oldContext = [EAGLContext currentContext]; + //GLuint oldRenderbuffer; + + if(oldContext != _context) + [EAGLContext setCurrentContext:_context]; + +#if DEBUG + CHECK_GL_ERROR(); +#endif + + //glGetIntegerv(GL_RENDERBUFFER_BINDING_OES, (GLint *) &oldRenderbuffer); + glBindRenderbufferOES(GL_RENDERBUFFER_OES, _renderbuffer); + //glBindRenderbufferOES(GL_RENDERBUFFER_OES, _framebuffer); + + if(![_context presentRenderbuffer:GL_RENDERBUFFER_OES]) + printf("Failed to swap renderbuffer in %s\n", __FUNCTION__); + + if(oldContext != _context) + [EAGLContext setCurrentContext:oldContext]; +} + +- (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 + +- (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 diff --git a/cocos2d/Support/FileUtils.h b/cocos2d/Support/FileUtils.h new file mode 100644 index 0000000..6190e1b --- /dev/null +++ b/cocos2d/Support/FileUtils.h @@ -0,0 +1,24 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import + + +/** Helper class to handle file operations */ +@interface FileUtils : NSObject +{ +} +/** converts a relative path to a full path */ ++(NSString*) fullPathFromRelativePath:(NSString*) relPath; +@end diff --git a/cocos2d/Support/FileUtils.m b/cocos2d/Support/FileUtils.m new file mode 100644 index 0000000..bf85a56 --- /dev/null +++ b/cocos2d/Support/FileUtils.m @@ -0,0 +1,41 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "FileUtils.h" + +@implementation FileUtils ++(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 diff --git a/cocos2d/Support/OpenGL_Internal.h b/cocos2d/Support/OpenGL_Internal.h new file mode 100755 index 0000000..8d875b1 --- /dev/null +++ b/cocos2d/Support/OpenGL_Internal.h @@ -0,0 +1,79 @@ +/* + +===== 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); }) + +/* 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/cocos2d/Support/PVRTexture.h b/cocos2d/Support/PVRTexture.h new file mode 100755 index 0000000..d248668 --- /dev/null +++ b/cocos2d/Support/PVRTexture.h @@ -0,0 +1,81 @@ +/* + +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 +#import + +@interface PVRTexture : NSObject +{ + NSMutableArray *_imageData; + + GLuint _name; + uint32_t _width, _height; + GLenum _internalFormat; + BOOL _hasAlpha; + + // cocos2d integration + BOOL _retainName; +} + +- (id)initWithContentsOfFile:(NSString *)path; +- (id)initWithContentsOfURL:(NSURL *)url; ++ (id)pvrTextureWithContentsOfFile:(NSString *)path; ++ (id)pvrTextureWithContentsOfURL:(NSURL *)url; + +@property (nonatomic,readonly) GLuint name; +@property (nonatomic,readonly) uint32_t width; +@property (nonatomic,readonly) uint32_t height; +@property (nonatomic,readonly) GLenum internalFormat; +@property (nonatomic,readonly) BOOL hasAlpha; + +// cocos2d integration +@property (nonatomic,readwrite) BOOL retainName; + +@end + + diff --git a/cocos2d/Support/PVRTexture.m b/cocos2d/Support/PVRTexture.m new file mode 100755 index 0000000..ac86ed7 --- /dev/null +++ b/cocos2d/Support/PVRTexture.m @@ -0,0 +1,280 @@ +/* + +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. + +*/ + +#import "PVRTexture.h" + +#define PVR_TEXTURE_FLAG_TYPE_MASK 0xff + +static char gPVRTexIdentifier[4] = "PVR!"; + +enum +{ + kPVRTextureFlagTypePVRTC_2 = 24, + kPVRTextureFlagTypePVRTC_4 +}; + +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 PVRTexture + +@synthesize name = _name; +@synthesize width = _width; +@synthesize height = _height; +@synthesize internalFormat = _internalFormat; +@synthesize hasAlpha = _hasAlpha; + +// cocos2d integration +@synthesize retainName = _retainName; + + +- (BOOL)unpackPVRData:(NSData *)data +{ + 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 bytes]; + + 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; + + if (formatFlags == kPVRTextureFlagTypePVRTC_4 || formatFlags == kPVRTextureFlagTypePVRTC_2) + { + [_imageData removeAllObjects]; + + if (formatFlags == kPVRTextureFlagTypePVRTC_4) + _internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + else if (formatFlags == kPVRTextureFlagTypePVRTC_2) + _internalFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + + _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 bytes]) + sizeof(PVRTexHeader); + + // Calculate the data size for each texture level and respect the minimum number of blocks + while (dataOffset < dataLength) + { + if (formatFlags == kPVRTextureFlagTypePVRTC_4) + { + blockSize = 4 * 4; // Pixel by pixel block size for 4bpp + widthBlocks = width / 4; + heightBlocks = height / 4; + bpp = 4; + } + else + { + blockSize = 8 * 4; // Pixel by pixel block size for 2bpp + widthBlocks = width / 8; + heightBlocks = height / 4; + bpp = 2; + } + + // Clamp to minimum number of blocks + if (widthBlocks < 2) + widthBlocks = 2; + if (heightBlocks < 2) + heightBlocks = 2; + + dataSize = widthBlocks * heightBlocks * ((blockSize * bpp) / 8); + + [_imageData addObject:[NSData dataWithBytes:bytes+dataOffset length:dataSize]]; + + dataOffset += dataSize; + + width = MAX(width >> 1, 1); + height = MAX(height >> 1, 1); + } + + success = TRUE; + } + + return success; +} + + +- (BOOL)createGLTexture +{ + int width = _width; + int height = _height; + NSData *data; + GLenum err; + + if ([_imageData count] > 0) + { + if (_name != 0) + glDeleteTextures(1, &_name); + + glGenTextures(1, &_name); + glBindTexture(GL_TEXTURE_2D, _name); + } + + for (NSUInteger i=0; i < [_imageData count]; i++) + { + data = [_imageData objectAtIndex:i]; + glCompressedTexImage2D(GL_TEXTURE_2D, i, _internalFormat, width, height, 0, [data length], [data bytes]); + + err = glGetError(); + if (err != GL_NO_ERROR) + { + NSLog(@"Error uploading compressed texture level: %d. glError: 0x%04X", i, err); + return FALSE; + } + + width = MAX(width >> 1, 1); + height = MAX(height >> 1, 1); + } + + [_imageData removeAllObjects]; + + return TRUE; +} + + +- (id)initWithContentsOfFile:(NSString *)path +{ + if((self = [super init])) + { + NSData *data = [NSData dataWithContentsOfFile:path]; + + _imageData = [[NSMutableArray alloc] initWithCapacity:10]; + + _name = 0; + _width = _height = 0; + _internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + _hasAlpha = FALSE; + + _retainName = NO; // cocos2d integration + + if (!data || ![self unpackPVRData:data] || ![self createGLTexture]) + { + [self release]; + self = nil; + } + } + + return self; +} + + +- (id)initWithContentsOfURL:(NSURL *)url +{ + if (![url isFileURL]) + { + [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 [PVRTexture pvrTextureWithContentsOfFile:[url path]]; +} + + +- (void)dealloc +{ + [_imageData release]; + + if (_name != 0 && ! _retainName ) + glDeleteTextures(1, &_name); + + [super dealloc]; +} + +@end + diff --git a/cocos2d/Support/TGAlib.h b/cocos2d/Support/TGAlib.h new file mode 100644 index 0000000..247084e --- /dev/null +++ b/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/cocos2d/Support/TGAlib.m b/cocos2d/Support/TGAlib.m new file mode 100644 index 0000000..b574d59 --- /dev/null +++ b/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/cocos2d/Support/Texture2D.h b/cocos2d/Support/Texture2D.h new file mode 100755 index 0000000..9373d81 --- /dev/null +++ b/cocos2d/Support/Texture2D.h @@ -0,0 +1,224 @@ +/* + +===== 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 +#import + +//CONSTANTS: + +/** Possible texture pixel formats */ +typedef enum { + kTexture2DPixelFormat_Automatic = 0, + //! 32-bit texture: RGBA8888 + kTexture2DPixelFormat_RGBA8888, + //! 16-bit texture: used with images that have alpha pre-multiplied + kTexture2DPixelFormat_RGB565, + //! 8-bit textures used as masks + kTexture2DPixelFormat_A8, + //! 16-bit textures: RGBA4444 + kTexture2DPixelFormat_RGBA4444, + //! 16-bit textures: RGB5A1 + kTexture2DPixelFormat_RGB5A1, +} Texture2DPixelFormat; + +/// Default pixel format: RGBA8888 +#define kTexture2DPixelFormat_Default kTexture2DPixelFormat_RGBA8888 + +//CLASS INTERFACES: + +/** Texture2D class + * This class allows to easily create OpenGL 2D textures from images, text or raw data. + * The created Texture2D object will always have power-of-two dimensions. + * Depending on how you create the Texture2D 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 Texture2D : NSObject +{ + GLuint _name; + CGSize _size; + NSUInteger _width, + _height; + Texture2DPixelFormat _format; + GLfloat _maxS, + _maxT; + BOOL _hasPremultipliedAlpha; +} +/** Intializes with a texture2d with data */ +- (id) initWithData:(const void*)data pixelFormat:(Texture2DPixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size; + +/** pixelFormat */ +@property(nonatomic,readonly) Texture2DPixelFormat pixelFormat; +/** width in pixels */ +@property(nonatomic,readonly) NSUInteger pixelsWide; +/** hight in pixels */ +@property(nonatomic,readonly) NSUInteger pixelsHigh; + +/** texture name */ +@property(nonatomic,readonly) GLuint name; + +/** content size */ +@property(nonatomic,readonly, nonatomic) CGSize contentSize; +/** texture max S */ +@property(nonatomic,readonly) GLfloat maxS; +/** texture max T */ +@property(nonatomic,readonly) GLfloat maxT; +/** whether or not the texture has their Alpha premultiplied */ +@property(nonatomic,readonly) BOOL hasPremultipliedAlpha; +@end + +/** +Drawing extensions to make it easy to draw basic quads using a Texture2D object. +These functions require GL_TEXTURE_2D and both GL_VERTEX_ARRAY and GL_TEXTURE_COORD_ARRAY client states to be enabled. +*/ +@interface Texture2D (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 Texture2D 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 Texture2D (Image) +/** Initializes a texture from a UIImage object */ +- (id) initWithImage:(UIImage *)uiImage; +@end + +/** +Extensions to make it easy to create a Texture2D 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 Texture2D (Text) +/** Initializes a texture from a string with dimensions, alignment, font name and font size */ +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(UITextAlignment)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 Texture2D object from a PVRTC file + Note that the generated textures are don't have their alpha premultiplied - use the blending mode (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). + */ +@interface Texture2D (PVRTC) +/** Initializes a texture from a PVRTC buffer */ +-(id) initWithPVRTCData: (const void*)data level:(int)level bpp:(int)bpp hasAlpha:(BOOL)hasAlpha length:(int)length; +/** Initializes a texture from a PVRTC file */ +-(id) initWithPVRTCFile: (NSString*) file; +@end + +/** + Extension to set the Min / Mag filter + */ +typedef struct _ccTexParams { + GLuint minFilter; + GLuint magFilter; + GLuint wrapS; + GLuint wrapT; +} ccTexParams; + +@interface Texture2D (GLFilter) +/** sets the min filter, mag filter, wrap s and wrap t texture parameters + @since v0.8 + */ +-(void) setTexParameters: (ccTexParams*) texParams; + +/** sets antialias texture parameters: + TEXTURE_MIN_FILTER = LINEAR + TEXTURE_MAG_FILTER = LINEAR + @since v0.8 + */ +- (void) setAntiAliasTexParameters; + +/** sets alias texture parameters: + TEXTURE_MIN_FILTER = NEAREST + TEXTURE_MAG_FILTER = NEAREST + @since v0.8 + */ +- (void) setAliasTexParameters; + +@end + +@interface Texture2D (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: RGBA8 (kTexture2DPixelFormat_RGBA8888) + - generate 16-bit textures: RGBA4 (default: kTexture2DPixelFormat_RGBA4444) + - generate 16-bit textures: RGB5A1 (kTexture2DPixelFormat_RGB5A1) + You can also use the following option, but you will lose the alpha channel: + - generate 16-bit textures: RGB565 (kTexture2DPixelFormat_RGB565) + + To use this function you MUST disable the "compres .PNG files" in XCode, otherwise all your .PNG images + will be pre-multiplied wihtout alpha channel. + @since v0.8 + */ ++(void) setDefaultAlphaPixelFormat:(Texture2DPixelFormat)format; + +/** returns the alpha pixel format + @since v0.8 + */ ++(Texture2DPixelFormat) defaultAlphaPixelFormat; +@end + + + diff --git a/cocos2d/Support/Texture2D.m b/cocos2d/Support/Texture2D.m new file mode 100755 index 0000000..7aba124 --- /dev/null +++ b/cocos2d/Support/Texture2D.m @@ -0,0 +1,525 @@ +/* + +===== 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.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 "ccMacros.h" +#import "Texture2D.h" +#import "PVRTexture.h" + + +//CONSTANTS: + +#define kMaxTextureSize 1024 +//#define kMaxTextureSize 2048 + +//CLASS IMPLEMENTATIONS: + + +// If the image has alpha, you can create RGBA8 (32-bit) or RGBA4 (16-bit) or RGB5A1 (16-bit) +// Default is: RGBA4444 (16-bit textures) +static Texture2DPixelFormat defaultAlphaPixelFormat = kTexture2DPixelFormat_Default; + +@implementation Texture2D + +@synthesize contentSize=_size, pixelFormat=_format, pixelsWide=_width, pixelsHigh=_height, name=_name, maxS=_maxS, maxT=_maxT; +@synthesize hasPremultipliedAlpha=_hasPremultipliedAlpha; +- (id) initWithData:(const void*)data pixelFormat:(Texture2DPixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size +{ +// GLint saveName; + if((self = [super init])) { + glGenTextures(1, &_name); +// glGetIntegerv(GL_TEXTURE_BINDING_2D, &saveName); + glBindTexture(GL_TEXTURE_2D, _name); + + [self setAntiAliasTexParameters]; + + // Specify OpenGL texture image + + switch(pixelFormat) + { + case kTexture2DPixelFormat_RGBA8888: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + break; + case kTexture2DPixelFormat_RGBA4444: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); + break; + case kTexture2DPixelFormat_RGB5A1: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, data); + break; + case kTexture2DPixelFormat_RGB565: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); + break; + case kTexture2DPixelFormat_A8: + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data); + break; + default: + [NSException raise:NSInternalInconsistencyException format:@""]; + + } + +// glBindTexture(GL_TEXTURE_2D, saveName); + + _size = size; + _width = width; + _height = height; + _format = pixelFormat; + _maxS = size.width / (float)width; + _maxT = size.height / (float)height; + + _hasPremultipliedAlpha = NO; + } + return self; +} + +- (void) dealloc +{ + CCLOG(@"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]; +} + +@end + +@implementation Texture2D (Image) + +- (id) initWithImage:(UIImage *)uiImage +{ + NSUInteger width, + height, + i; + CGContextRef context = nil; + void* data = nil;; + CGColorSpaceRef colorSpace; + void* tempData; + unsigned int* inPixel32; + unsigned short* outPixel16; + BOOL hasAlpha; + CGImageAlphaInfo info; + CGAffineTransform transform; + CGSize imageSize; + Texture2DPixelFormat pixelFormat; + CGImageRef image; + BOOL sizeToFit = NO; + + + image = [uiImage CGImage]; + + if(image == NULL) { + [self release]; + NSLog(@"Image is Null"); + return nil; + } + + + info = CGImageGetAlphaInfo(image); + hasAlpha = ((info == kCGImageAlphaPremultipliedLast) || (info == kCGImageAlphaPremultipliedFirst) || (info == kCGImageAlphaLast) || (info == kCGImageAlphaFirst) ? YES : NO); + + size_t bpp = CGImageGetBitsPerComponent(image); + if(CGImageGetColorSpace(image)) { + if(hasAlpha || bpp >= 8) + pixelFormat = defaultAlphaPixelFormat; + else + pixelFormat = kTexture2DPixelFormat_RGB565; + } else //NOTE: No colorspace means a mask image + pixelFormat = kTexture2DPixelFormat_A8; + + imageSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image)); + transform = CGAffineTransformIdentity; + + width = imageSize.width; + + if((width != 1) && (width & (width - 1))) { + i = 1; + while((sizeToFit ? 2 * i : i) < width) + i *= 2; + width = i; + } + height = imageSize.height; + if((height != 1) && (height & (height - 1))) { + i = 1; + while((sizeToFit ? 2 * i : i) < height) + i *= 2; + height = i; + } + + // iPhone 3GS supports 2048 textures, so the maxtexturesize should be a variable, not a hardcoded value + NSAssert2( (width <= kMaxTextureSize) && (height <= kMaxTextureSize), @"Image is bigger than the supported %d x %d", kMaxTextureSize, kMaxTextureSize); + +// while((width > kMaxTextureSize) || (height > kMaxTextureSize)) { +// width /= 2; +// height /= 2; +// transform = CGAffineTransformScale(transform, 0.5f, 0.5f); +// imageSize.width *= 0.5f; +// imageSize.height *= 0.5f; +// } + + // Create the bitmap graphics context + + switch(pixelFormat) { + case kTexture2DPixelFormat_RGBA8888: + case kTexture2DPixelFormat_RGBA4444: + case kTexture2DPixelFormat_RGB5A1: + colorSpace = CGColorSpaceCreateDeviceRGB(); + data = malloc(height * width * 4); + context = CGBitmapContextCreate(data, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); + CGColorSpaceRelease(colorSpace); + break; + case kTexture2DPixelFormat_RGB565: + colorSpace = CGColorSpaceCreateDeviceRGB(); + data = malloc(height * width * 4); + context = CGBitmapContextCreate(data, width, height, 8, 4 * width, colorSpace, kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big); + CGColorSpaceRelease(colorSpace); + break; + case kTexture2DPixelFormat_A8: + data = malloc(height * width); + context = CGBitmapContextCreate(data, width, height, 8, width, NULL, kCGImageAlphaOnly); + break; + default: + [NSException raise:NSInternalInconsistencyException format:@"Invalid pixel format"]; + } + + + CGContextClearRect(context, CGRectMake(0, 0, width, height)); + CGContextTranslateCTM(context, 0, height - imageSize.height); + + if(!CGAffineTransformIsIdentity(transform)) + CGContextConcatCTM(context, transform); + CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image); + + // Repack the pixel data into the right format + + if(pixelFormat == kTexture2DPixelFormat_RGB565) { + //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB" + tempData = malloc(height * width * 2); + inPixel32 = (unsigned int*)data; + outPixel16 = (unsigned short*)tempData; + for(i = 0; i < width * height; ++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 == kTexture2DPixelFormat_RGBA4444) { + //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRGGGGBBBBAAAA" + tempData = malloc(height * width * 2); + inPixel32 = (unsigned int*)data; + outPixel16 = (unsigned short*)tempData; + for(i = 0; i < width * height; ++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 == kTexture2DPixelFormat_RGB5A1) { + //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGBBBBBA" + tempData = malloc(height * width * 2); + inPixel32 = (unsigned int*)data; + outPixel16 = (unsigned short*)tempData; + for(i = 0; i < width * height; ++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:width pixelsHigh:height contentSize:imageSize]; + + // should be after calling super init + _hasPremultipliedAlpha = (info == kCGImageAlphaPremultipliedLast || info == kCGImageAlphaPremultipliedFirst); + + CGContextRelease(context); + free(data); + + return self; +} + +@end + +@implementation Texture2D (Text) + +- (id) initWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size +{ + CGSize dim = [string sizeWithFont: [UIFont fontWithName:name size:size]]; + return [self initWithString:string dimensions:dim alignment:UITextAlignmentCenter fontName:name fontSize:size]; +} + +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(UITextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size +{ + NSUInteger width, + height, + i; + CGContextRef context; + void* data; + CGColorSpaceRef colorSpace; + UIFont * font; + + font = [UIFont fontWithName:name size:size]; + + width = dimensions.width; + if((width != 1) && (width & (width - 1))) { + i = 1; + while(i < width) + i *= 2; + width = i; + } + height = dimensions.height; + if((height != 1) && (height & (height - 1))) { + i = 1; + while(i < height) + i *= 2; + height = i; + } + + colorSpace = CGColorSpaceCreateDeviceGray(); + data = calloc(height, width); + context = CGBitmapContextCreate(data, width, height, 8, width, colorSpace, kCGImageAlphaNone); + CGColorSpaceRelease(colorSpace); + + + CGContextSetGrayFillColor(context, 1.0f, 1.0f); + CGContextTranslateCTM(context, 0.0f, height); + CGContextScaleCTM(context, 1.0f, -1.0f); //NOTE: NSString draws in UIKit referential i.e. renders upside-down compared to CGBitmapContext referential + UIGraphicsPushContext(context); + [string drawInRect:CGRectMake(0, 0, dimensions.width, dimensions.height) withFont:font lineBreakMode:UILineBreakModeWordWrap alignment:alignment]; + UIGraphicsPopContext(); + + self = [self initWithData:data pixelFormat:kTexture2DPixelFormat_A8 pixelsWide:width pixelsHigh:height contentSize:dimensions]; + + CGContextRelease(context); + free(data); + + return self; +} + +@end + +@implementation Texture2D (Drawing) + +- (void) drawAtPoint:(CGPoint)point +{ + GLfloat coordinates[] = { 0.0f, _maxT, + _maxS, _maxT, + 0.0f, 0.0f, + _maxS, 0.0f }; + GLfloat width = (GLfloat)_width * _maxS, + height = (GLfloat)_height * _maxT; + +#if 0 + GLfloat vertices[] = { -width / 2 + point.x, -height / 2 + point.y, 0.0f, + width / 2 + point.x, -height / 2 + point.y, 0.0f, + -width / 2 + point.x, height / 2 + point.y, 0.0f, + width / 2 + point.x, height / 2 + point.y, 0.0f }; + +#else // anchor is done by cocos2d automagically + GLfloat vertices[] = { point.x, point.y, 0.0f, + width + point.x, point.y, 0.0f, + point.x, height + point.y, 0.0f, + width + point.x, height + point.y, 0.0f }; +#endif + + glBindTexture(GL_TEXTURE_2D, _name); + glVertexPointer(3, GL_FLOAT, 0, vertices); + glTexCoordPointer(2, GL_FLOAT, 0, coordinates); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +} + + +- (void) drawInRect:(CGRect)rect +{ + GLfloat coordinates[] = { 0.0f, _maxT, + _maxS, _maxT, + 0.0f, 0.0f, + _maxS, 0.0f }; + GLfloat vertices[] = { rect.origin.x, rect.origin.y, /*0.0f,*/ + rect.origin.x + rect.size.width, rect.origin.y, /*0.0f,*/ + rect.origin.x, rect.origin.y + rect.size.height, /*0.0f,*/ + rect.origin.x + rect.size.width, rect.origin.y + rect.size.height, /*0.0f*/ }; + + glBindTexture(GL_TEXTURE_2D, _name); + glVertexPointer(2, GL_FLOAT, 0, vertices); + glTexCoordPointer(2, GL_FLOAT, 0, coordinates); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +} + +@end + +@implementation Texture2D (PVRTC) +-(id) initWithPVRTCData: (const void*)data level:(int)level bpp:(int)bpp hasAlpha:(BOOL)hasAlpha length:(int)length +{ +// GLint saveName; + + if((self = [super init])) { + glGenTextures(1, &_name); +// glGetIntegerv(GL_TEXTURE_BINDING_2D, &saveName); + glBindTexture(GL_TEXTURE_2D, _name); + + [self setAntiAliasTexParameters]; + + GLenum format; + GLsizei size = length * length * bpp / 8; + if(hasAlpha) { + format = (bpp == 4) ? GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + } else { + format = (bpp == 4) ? GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG : GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + } + if(size < 32) { + size = 32; + } + glCompressedTexImage2D(GL_TEXTURE_2D, level, format, length, length, 0, size, data); + +// glBindTexture(GL_TEXTURE_2D, saveName); + + _size = CGSizeMake(length, length); + _width = length; + _height = length; + _maxS = 1.0f; + _maxT = 1.0f; + } + return self; +} + +-(id) initWithPVRTCFile: (NSString*) file +{ + + if( (self = [super init]) ) { + PVRTexture *pvr = [[PVRTexture alloc] initWithContentsOfFile:file]; + pvr.retainName = YES; // don't dealloc texture on release + + _name = pvr.name; // texture id + _maxS = 1.0f; + _maxT = 1.0f; + _width = pvr.width; // width + _height = pvr.height; // height + _size = CGSizeMake(_width, _height); + + [pvr release]; + + [self setAntiAliasTexParameters]; + } + return self; +} +@end + +// +// Use to apply MIN/MAG filter +// +@implementation Texture2D (GLFilter) + +-(void) setTexParameters: (ccTexParams*) texParams +{ + 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 + +// +// Texture options for images that contains alpha +// +@implementation Texture2D (PixelFormat) ++(void) setDefaultAlphaPixelFormat:(Texture2DPixelFormat)format +{ + defaultAlphaPixelFormat = format; +} + ++(Texture2DPixelFormat) defaultAlphaPixelFormat +{ + return defaultAlphaPixelFormat; +} +@end diff --git a/cocos2d/Support/TransformUtils.h b/cocos2d/Support/TransformUtils.h new file mode 100644 index 0000000..e5e359c --- /dev/null +++ b/cocos2d/Support/TransformUtils.h @@ -0,0 +1,19 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Valentin Milea + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import +#import + +void CGAffineToGL(const CGAffineTransform *t, GLfloat *m); +void GLToCGAffine(const GLfloat *m, CGAffineTransform *t); diff --git a/cocos2d/Support/TransformUtils.m b/cocos2d/Support/TransformUtils.m new file mode 100644 index 0000000..fba85a8 --- /dev/null +++ b/cocos2d/Support/TransformUtils.m @@ -0,0 +1,34 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Valentin Milea + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#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/cocos2d/Support/ZipUtils.h b/cocos2d/Support/ZipUtils.h new file mode 100644 index 0000000..af69ca8 --- /dev/null +++ b/cocos2d/Support/ZipUtils.h @@ -0,0 +1,41 @@ +/* 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 + +#ifdef __cplusplus +extern "C" { +#endif + +/** @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 inflateMemory(unsigned char *in, unsigned int inLength, unsigned char **out); + + +#ifdef __cplusplus +} +#endif + +#endif // __CC_ZIP_UTILS_H diff --git a/cocos2d/Support/ZipUtils.m b/cocos2d/Support/ZipUtils.m new file mode 100644 index 0000000..e288f4f --- /dev/null +++ b/cocos2d/Support/ZipUtils.m @@ -0,0 +1,125 @@ +/* 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 "ccMacros.h" + +int inflateMemory_(unsigned char *in, unsigned int inLength, unsigned char **out, unsigned int *outLength) +{ +#if 1 + /* 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) { + + // memory in iPhone is precious + // Should buffer factor be 1.5 instead of 2 ? +#define BUFFER_INC_FACTOR (2) + unsigned char *tmp = realloc(*out, bufferSize * BUFFER_INC_FACTOR); + + /* not enough memory, ouch */ + if (! tmp ) { + CCLOG(@"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; +#else + return 0; +#endif +} + +int inflateMemory(unsigned char *in, unsigned int inLength, unsigned char **out) +{ +#if 1 + unsigned int outLength = 0; + int err = inflateMemory_(in, inLength, out, &outLength); + + if (err != Z_OK || *out == NULL) { + if (err == Z_MEM_ERROR) + CCLOG(@"ZipUtils: Out of memory while decompressing map data!"); + + else if (err == Z_VERSION_ERROR) + CCLOG(@"ZipUtils: Incompatible zlib version!"); + + else if (err == Z_DATA_ERROR) + CCLOG(@"ZipUtils: Incorrect zlib compressed data!"); + + else + CCLOG(@"ZipUtils: Unknown error while decompressing map data!"); + + free(*out); + *out = NULL; + outLength = 0; + } + + return outLength; +#else + return 0; +#endif +} diff --git a/cocos2d/Support/base64.c b/cocos2d/Support/base64.c new file mode 100644 index 0000000..5fdd30a --- /dev/null +++ b/cocos2d/Support/base64.c @@ -0,0 +1,90 @@ +/* + 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/cocos2d/Support/base64.h b/cocos2d/Support/base64.h new file mode 100644 index 0000000..a59bbd1 --- /dev/null +++ b/cocos2d/Support/base64.h @@ -0,0 +1,34 @@ +/* + 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/cocos2d/Support/ccArray.h b/cocos2d/Support/ccArray.h new file mode 100644 index 0000000..c730b58 --- /dev/null +++ b/cocos2d/Support/ccArray.h @@ -0,0 +1,212 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +/* 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 + */ + +#ifndef CC_ARRAY_H +#define CC_ARRAY_H + +#import + +#import +#import + +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); +} + +/** 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]; + + for( NSUInteger last = --arr->num; index < last; index++) + arr->arr[index] = arr->arr[index + 1]; +} + +/** 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]; +} + +/** 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]; +} + +#endif // CC_ARRAY_H diff --git a/cocos2d/Support/ccHashSet.h b/cocos2d/Support/ccHashSet.h new file mode 100644 index 0000000..2cf1f1a --- /dev/null +++ b/cocos2d/Support/ccHashSet.h @@ -0,0 +1,127 @@ +/* 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. + */ + +// ccHashSet uses a chained hashtable implementation. + +#ifndef __CC_HASH_SET_H +#define __CC_HASH_SET_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + @file + Based on Chipmunk cpHashSet. + ccHashSet is a faster alternative to NSMutableDictionary. The main difference between ccHashSet and NSDictionary + is that ccHashSet uses pointers, while NSMutableDictionary uses NSObject. + ccHashSet is 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 + + If you use the CC_HASH_INT, it will use the One-At-A-Time hash function. + When the number of entries of the ccHashSet are greater than the size * 3 of the ccHashSet, then the ccHashSet size will be doubled. + Ideally, the ccHashSet will have no more than 3 entries per hash value. + */ + + +/** Performs the Shift-Add-Xor hash function +*/ +unsigned int cc_hash_sax ( void *key, int len ); + +/** Performns the One-At-a-time hash function + */ +unsigned int cc_hash_oat ( void *key, int len ); + +#define CC_HASH_INT(A) cc_hash_oat(&A,sizeof(A)) + +// ccHashSetBin's form the linked lists in the chained hash table. +typedef struct ccHashSetBin { + // Pointer to the element. + void *elt; + // Hash value of the element. + unsigned int hash; + // Next element in the chain. + struct ccHashSetBin *next; +} ccHashSetBin; + +// Equality function. Returns true if ptr is equal to elt. +typedef int (*ccHashSetEqlFunc)(void *ptr, void *elt); +// Iterator function for a hashset. +typedef void (*ccHashSetIterFunc)(void *elt, void *data); +// Reject function. Returns true if elt should be dropped. +typedef int (*ccHashSetRejectFunc)(void *elt, void *data); + +/** ccHashSet type */ +typedef struct ccHashSet { + // Number of elements stored in the table. + int entries; + // Number of cells in the table. + int size; + + ccHashSetEqlFunc eql; + + // Default value returned by ccHashSetFind() when no element is found. + // Defaults to NULL. + void *default_value; + + ccHashSetBin **table; +} ccHashSet; + +/** Destroy the ccHashSet */ +void ccHashSetDestroy(ccHashSet *set); +/** Free the ccHashSet */ +void ccHashSetFree(ccHashSet *set); + +/** Allocates the ccHashSet */ +ccHashSet *ccHashSetAlloc(void); +/** Initializes the ccHashSet with a size and a equal function */ +ccHashSet *ccHashSetInit(ccHashSet *set, int size, ccHashSetEqlFunc eqlFunc); +/** Allocates and initialize a ccHashSet with a size and a equal function */ +ccHashSet *ccHashSetNew(int size, ccHashSetEqlFunc eqlFunc); + +/** Insert an element into the set, returns the element. +If it doesn't already exist, the transformation function is applied. + */ +void *ccHashSetInsert(ccHashSet *set, unsigned int hash, void *ptr, void *data); +/** Remove and return an element from the set. + */ +void *ccHashSetRemove(ccHashSet *set, unsigned int hash, void *ptr); +/** Find an element in the set. Returns the default value if the element isn't found. + */ +void *ccHashSetFind(ccHashSet *set, unsigned int hash, void *ptr); + +/** Iterate over a hashset. + */ +void ccHashSetEach(ccHashSet *set, ccHashSetIterFunc func, void *data); +/** Iterate over a hashset while rejecting certain elements. + */ +void ccHashSetReject(ccHashSet *set, ccHashSetRejectFunc func, void *data); + +#ifdef __cplusplus +} +#endif + +#endif // __CC_HASH_SET_H + diff --git a/cocos2d/Support/ccHashSet.m b/cocos2d/Support/ccHashSet.m new file mode 100644 index 0000000..af49ae0 --- /dev/null +++ b/cocos2d/Support/ccHashSet.m @@ -0,0 +1,296 @@ +/* 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. + */ + + +#include +#include +#include + +#import "ccHashSet.h" + +// Used for resizing hash tables. +// Values approximately double. + +static int primes[] = { + 5, //2^2 + 1 + 11, //2^3 + 3 + 17, //2^4 + 1 + 37, //2^5 + 5 + 67, //2^6 + 3 + 131, //2^7 + 3 + 257, //2^8 + 1 + 521, //2^9 + 9 + 1031, //2^10 + 7 + 2053, //2^11 + 5 + 4099, //2^12 + 3 + 8209, //2^13 + 17 + 16411, //2^14 + 27 + 32771, //2^15 + 3 + 65537, //2^16 + 1 + 131101, //2^17 + 29 + 262147, //2^18 + 3 + 524309, //2^19 + 21 + 1048583, //2^20 + 7 + 2097169, //2^21 + 17 + 4194319, //2^22 + 15 + 8388617, //2^23 + 9 + 16777259, //2^24 + 43 + 33554467, //2^25 + 35 + 67108879, //2^26 + 15 + 134217757, //2^27 + 29 + 268435459, //2^28 + 3 + 536870923, //2^29 + 11 + 1073741827, //2^30 + 3 + 0, +}; + +static int +next_prime(int n) +{ + int i = 0; + while(n > primes[i]){ + i++; + assert(primes[i]); // realistically this should never happen + } + + return primes[i]; +} + +unsigned int cc_hash_sax( void *key, int len ) +{ + unsigned char *p = key; + unsigned int h = 0; + int i; + for ( i = 0; i < len; i++ ) + h ^= ( h << 5 ) + ( h >> 2 ) + p[i]; + + return h; +} +unsigned int cc_hash_oat ( void *key, int len ) +{ + unsigned char *p = key; + unsigned int h = 0; + int i; + + for ( i = 0; i < len; i++ ) { + h += p[i]; + h += ( h << 10 ); + h ^= ( h >> 6 ); + } + h += ( h << 3 ); + h ^= ( h >> 11 ); + h += ( h << 15 ); + return h; +} + +void +ccHashSetDestroy(ccHashSet *set) +{ + // Free the chains. + for(int i=0; isize; i++){ + // Free the bins in the chain. + ccHashSetBin *bin = set->table[i]; + while(bin){ + ccHashSetBin *next = bin->next; + free(bin); + bin = next; + } + } + + // Free the table. + free(set->table); +} + +void +ccHashSetFree(ccHashSet *set) +{ + if(set) ccHashSetDestroy(set); + free(set); +} + +ccHashSet * +ccHashSetAlloc(void) +{ + return (ccHashSet *)calloc(1, sizeof(ccHashSet)); +} + +ccHashSet * +ccHashSetInit(ccHashSet *set, int size, ccHashSetEqlFunc eqlFunc) +{ + set->size = next_prime(size); + set->entries = 0; + + set->eql = eqlFunc; + + set->default_value = NULL; + + set->table = (ccHashSetBin **)calloc(set->size, sizeof(ccHashSetBin *)); + + return set; +} + +ccHashSet * +ccHashSetNew(int size, ccHashSetEqlFunc eqlFunc) +{ + return ccHashSetInit(ccHashSetAlloc(), size, eqlFunc); +} + +static int +setIsFull(ccHashSet *set) +{ + return (set->entries >= set->size * 3.0f); +} + +static void +ccHashSetResize(ccHashSet *set) +{ + // Get the next approximate doubled prime. + int newSize = next_prime(set->size + 1); + // Allocate a new table. + ccHashSetBin **newTable = (ccHashSetBin **)calloc(newSize, sizeof(ccHashSetBin *)); + + // Iterate over the chains. + for(int i=0; isize; i++){ + // Rehash the bins into the new table. + ccHashSetBin *bin = set->table[i]; + while(bin){ + ccHashSetBin *next = bin->next; + + int index = bin->hash%newSize; + bin->next = newTable[index]; + newTable[index] = bin; + + bin = next; + } + } + + free(set->table); + + set->table = newTable; + set->size = newSize; +} + +void * +ccHashSetInsert(ccHashSet *set, unsigned int hash, void *ptr, void *data) +{ + int index = hash%set->size; + + // Find the bin with the matching element. + ccHashSetBin *bin = set->table[index]; + while(bin && !set->eql(ptr, bin->elt)) + bin = bin->next; + + // Create it necessary. + if(!bin){ + bin = (ccHashSetBin *)malloc(sizeof(ccHashSetBin)); + bin->hash = hash; + bin->elt = ptr; + + bin->next = set->table[index]; + set->table[index] = bin; + + set->entries++; + + // Resize the set if it's full. + if(setIsFull(set)) + ccHashSetResize(set); + } + + return bin->elt; +} + +void * +ccHashSetRemove(ccHashSet *set, unsigned int hash, void *ptr) +{ + int index = hash%set->size; + + // Pointer to the previous bin pointer. + ccHashSetBin **prev_ptr = &set->table[index]; + // Pointer the the current bin. + ccHashSetBin *bin = set->table[index]; + + // Find the bin + while(bin && !set->eql(ptr, bin->elt)){ + prev_ptr = &bin->next; + bin = bin->next; + } + + // Remove it if it exists. + if(bin){ + // Update the previos bin pointer to point to the next bin. + (*prev_ptr) = bin->next; + set->entries--; + + void *return_value = bin->elt; + free(bin); + return return_value; + } + + return NULL; +} + +void * +ccHashSetFind(ccHashSet *set, unsigned int hash, void *ptr) +{ + int index = hash%set->size; + +// printf("bucket index: %3d - %x\n", index, hash); + ccHashSetBin *bin = set->table[index]; + while(bin && !set->eql(ptr, bin->elt)) + bin = bin->next; + + return (bin ? bin->elt : set->default_value); +} + +void +ccHashSetEach(ccHashSet *set, ccHashSetIterFunc func, void *data) +{ + for(int i=0; isize; i++){ + ccHashSetBin *bin; + for(bin = set->table[i]; bin; bin = bin->next) + func(bin->elt, data); + } +} + +void +ccHashSetReject(ccHashSet *set, ccHashSetRejectFunc func, void *data) +{ + // Iterate over all the chains. + for(int i=0; isize; i++){ + // The rest works similarly to ccHashSetRemove() above. + ccHashSetBin **prev_ptr = &set->table[i]; + ccHashSetBin *bin = set->table[i]; + while(bin){ + ccHashSetBin *next = bin->next; + + if(func(bin->elt, data)){ + prev_ptr = &bin->next; + } else { + (*prev_ptr) = next; + + set->entries--; + free(bin); + } + + bin = next; + } + } +} diff --git a/cocos2d/Support/glu.c b/cocos2d/Support/glu.c new file mode 100755 index 0000000..cad7139 --- /dev/null +++ b/cocos2d/Support/glu.c @@ -0,0 +1,112 @@ +// +// cocos2d (incomplete) GLU implementation +// +// gluLookAt and gluPerspective from: +// http://jet.ro/creations (San Angeles Observation) +// +// + +#import +#import +#import "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 + { + int a; + GLfloat fixedM[16]; + for (a = 0; a < 16; ++a) + fixedM[a] = m[a]; + glMultMatrixf(fixedM); + } + + /* Translate Eye to Origin */ + glTranslatef(-eyex, -eyey, -eyez); +} + + diff --git a/cocos2d/Support/glu.h b/cocos2d/Support/glu.h new file mode 100755 index 0000000..70b3721 --- /dev/null +++ b/cocos2d/Support/glu.h @@ -0,0 +1,21 @@ +// +// cocos2d GLU implementation +// +// implementation of GLU functions +// +#ifndef __COCOS2D_GLU_H +#define __COCOS2D_GLU_H + +#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 /* __COCOS2D_GLU_H */ diff --git a/cocos2d/TMXTiledMap.h b/cocos2d/TMXTiledMap.h new file mode 100644 index 0000000..d955e43 --- /dev/null +++ b/cocos2d/TMXTiledMap.h @@ -0,0 +1,166 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + * TMX Tiled Map support: + * http://www.mapeditor.org + * + */ + +#import "AtlasNode.h" +#import "AtlasSpriteManager.h" + + +@class TMXMapInfo; +@class TMXLayer; +@class TMXLayerInfo; +@class TMXTilesetInfo; + +/** Possible oritentations of the TMX map */ +enum +{ + /** Orthogonal orientation */ + TMXOrientationOrtho, + + /** Hexagonal orientation */ + TMXOrientationHex, + + /** Isometric orientation */ + TMXOrientationIso, +}; + +/** TMXTiledMap 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. + + Features: + - Each tile will be treated as an AtlasSprite + - Each tile can be rotated / moved / scaled / tinted / "opacitied" + - 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 TextureMgr + - Each tile will have a unique tag + - Each tile will have a unique z value. top-left: z=1, bottom-right: z=max z + + Limitations: + - It only supports one tileset. + - 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 TMXLayer (subclass of AtlasSpriteManager). If you have 5 layers, then 5 TMXLayer 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 (TMXLayer objects) at runtime by: + - [map getChildByTag: tag_number]; // 0=1st layer, 1=2nd layer, 2=3rd layer, etc... + - [map layerNamed: name_of_the_layer]; + + @since v0.8.1 + */ +@interface TMXTiledMap : CocosNode +{ + CGSize mapSize_; + CGSize tileSize_; + int mapOrientation_; +} + +/** 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; + +/** 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 */ +-(TMXLayer*) layerNamed:(NSString *)layerName; +@end + + +/** TMXLayer represents the TMX layer. + + It is a subclass of AtlasSpriteManager, so each "tile" is represented by an AtlasSprite. + The benefits of using AtlasSprite objects as tiles are: + - tiles (AtlasSprite) can be rotated/scaled/moved with a nice API + + @since v0.8.1 + */ +@interface TMXLayer : AtlasSpriteManager +{ + TMXTilesetInfo *tileset_; + NSString *layerName_; + CGSize layerSize_; + CGSize mapTileSize_; + unsigned int *tiles_; + int layerOrientation_; +} +/** 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) unsigned int *tiles; +/** Tilset information for the layer */ +@property (nonatomic,readwrite,retain) TMXTilesetInfo *tileset; +/** Layer orientation, which is the same as the map orientation */ +@property (nonatomic,readwrite) int layerOrientation; + + +/** creates a TMX Layer with an tileset info, a layer info and a map info */ ++(id) layerWithTilesetInfo:(TMXTilesetInfo*)tilesetInfo layerInfo:(TMXLayerInfo*)layerInfo mapInfo:(TMXMapInfo*)mapInfo; +/** initializes a TMX Layer with a tileset info, a layer info and a map info */ +-(id) initWithTilesetInfo:(TMXTilesetInfo*)tilesetInfo layerInfo:(TMXLayerInfo*)layerInfo mapInfo:(TMXMapInfo*)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; + +/** adds a tile given a GID and a tile coordinate. + The tile will be added with the z and tag the belongs to the tile coordinate + */ +-(AtlasSprite*) addTileForGID:(unsigned int)gid at:(CGPoint)pos; + +/** returns the tile (AtlasSprite) at a given a tile coordinate */ +-(AtlasSprite*) 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]) + */ +-(unsigned int) 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:(unsigned int)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; +@end + + diff --git a/cocos2d/TMXTiledMap.m b/cocos2d/TMXTiledMap.m new file mode 100644 index 0000000..191b982 --- /dev/null +++ b/cocos2d/TMXTiledMap.m @@ -0,0 +1,310 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + * TMX Tiled Map support: + * http://www.mapeditor.org + * + */ + +#import "TMXTiledMap.h" +#import "TMXXMLParser.h" +#import "AtlasSprite.h" +#import "Support/CGPointExtension.h" + +#pragma mark - +#pragma mark TMXTiledMap + +@interface TMXTiledMap (Private) +-(id) parseLayer:(TMXLayerInfo*)layer tileset:(TMXTilesetInfo*)tileset map:(TMXMapInfo*)mapInfo; +@end + +@implementation TMXTiledMap +@synthesize mapSize=mapSize_; +@synthesize tileSize=tileSize_; +@synthesize mapOrientation=mapOrientation_; + ++(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]; + + TMXMapInfo *mapInfo = [TMXMapInfo formatWithTMXFile:tmxFile]; + mapSize_ = mapInfo->mapSize; + tileSize_ = mapInfo->tileSize; + mapOrientation_ = mapInfo->orientation; + + NSAssert( [mapInfo->tilesets count] == 1, @"TMXTiledMap: only supports 1 tileset"); + + int idx=0; + TMXTilesetInfo *tileset = [mapInfo->tilesets objectAtIndex:0]; + + for( TMXLayerInfo *layerInfo in mapInfo->layers ) { + if( layerInfo->visible ) { + id child = [self parseLayer:layerInfo tileset:tileset 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 +{ + [super dealloc]; +} + +// private +-(id) parseLayer:(TMXLayerInfo*)layerInfo tileset:(TMXTilesetInfo*)tileset map:(TMXMapInfo*)mapInfo +{ + TMXLayer *layer = [TMXLayer layerWithTilesetInfo:tileset layerInfo:layerInfo mapInfo:mapInfo]; + + // tell the layerinfo to release the ownership of the tiles map. + layerInfo->ownTiles = NO; + + // XXX: quick hack that sets in the tileset the size of the image + tileset->imageSize = [[layer texture] contentSize]; + + // By default all the tiles are aliased + // pros: + // - easier to render + // cons: + // - difficult to scale / rotate / etc. + [[layer texture] setAliasTexParameters]; + + CFByteOrder o = CFByteOrderGetCurrent(); + + for( unsigned int y=0; y < layerInfo->layerSize.height; y++ ) { + for( unsigned int x=0; x < layerInfo->layerSize.width; x++ ) { + + unsigned int pos = x + layerInfo->layerSize.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 ) { + AtlasSprite *tile = [layer addTileForGID:gid at:ccp(x,y)]; + [tile position]; // XXX + } + } + } + + return layer; +} + +-(TMXLayer*) layerNamed:(NSString *)layerName +{ + for( TMXLayer *layer in children ) { + if( [layer.layerName isEqual:layerName] ) + return layer; + } + + // layer not found + return nil; +} +@end + +#pragma mark - +#pragma mark TMXLayer + +@interface TMXLayer (Private) +-(CGPoint) positionForIsoAt:(CGPoint)pos; +-(CGPoint) positionForOrthoAt:(CGPoint)pos; +-(CGPoint) positionForHexAt:(CGPoint)pos; +@end + +@implementation TMXLayer +@synthesize layerSize = layerSize_, layerName = layerName_, tiles=tiles_; +@synthesize tileset=tileset_; +@synthesize layerOrientation=layerOrientation_; +@synthesize mapTileSize=mapTileSize_; + ++(id) layerWithTilesetInfo:(TMXTilesetInfo*)tilesetInfo layerInfo:(TMXLayerInfo*)layerInfo mapInfo:(TMXMapInfo*)mapInfo +{ + return [[[self alloc] initWithTilesetInfo:tilesetInfo layerInfo:layerInfo mapInfo:mapInfo] autorelease]; +} + +-(id) initWithTilesetInfo:(TMXTilesetInfo*)tilesetInfo layerInfo:(TMXLayerInfo*)layerInfo mapInfo:(TMXMapInfo*)mapInfo +{ + // XXX: is 35% a good estimate ? + float totalNumberOfTiles = layerInfo->layerSize.width * layerInfo->layerSize.height; + float capacity = totalNumberOfTiles * 0.35f + 1; // 35 percent is occupied ? + if((self=[super initWithFile:tilesetInfo->sourceImage capacity:capacity])) { + self.layerName = layerInfo->name; + self.layerSize = layerInfo->layerSize; + self.tiles = layerInfo->tiles; + self.tileset = tilesetInfo; + self.mapTileSize = mapInfo->tileSize; + self.layerOrientation = mapInfo->orientation; + + [self setContentSize: CGSizeMake( layerSize_.width * mapTileSize_.width, layerSize_.height * mapTileSize_.height )]; + } + return self; +} + +- (void) dealloc +{ + [layerName_ release]; + [tileset_ release]; + + if( tiles_ ) { + free(tiles_); + tiles_ = NULL; + } + + [super dealloc]; +} + +-(void) releaseMap +{ + if( tiles_) { + free( tiles_); + tiles_ = NULL; + } +} + +#pragma mark TMXLayer - obtaining tiles/gids + +-(AtlasSprite*) tileAt:(CGPoint)pos +{ + NSAssert( pos.x < layerSize_.width && pos.y <= layerSize_.height, @"TMXLayer: invalid position"); + + int t = pos.x + pos.y * layerSize_.width; + return (AtlasSprite*) [self getChildByTag:t]; +} + +-(unsigned int) tileGIDAt:(CGPoint)pos +{ + NSAssert( pos.x < layerSize_.width && pos.y <= layerSize_.height, @"TMXLayer: invalid position"); + NSAssert( tiles_ != NULL, @"TMXLayer: the tiles map has been released"); + + int idx = pos.x + pos.y * layerSize_.width; + return tiles_[ idx ]; +} + +#pragma mark TMXLayer - adding / remove tiles + +-(void) setTileGID:(unsigned int)gid at:(CGPoint)pos +{ + NSAssert( pos.x < layerSize_.width && pos.y <= layerSize_.height, @"TMXLayer: invalid position"); + NSAssert( tiles_ != NULL, @"TMXLayer: the tiles map has been released"); + + unsigned int currentGID = [self tileGIDAt:pos]; + + if( currentGID != gid ) { + AtlasSprite *tile = [self tileAt:pos]; + if( ! tile ) { + tile = [self addTileForGID:gid at:pos]; + } else { + CGRect rect = [tileset_ tileForGID:gid]; + [tile setTextureRect:rect]; + } + + // update gid on map + int idx = pos.x + pos.y * layerSize_.width; + tiles_[ idx ] = gid; + } +} + +-(AtlasSprite*) addTileForGID:(unsigned int)gid at:(CGPoint)pos +{ + CGRect rect = [tileset_ tileForGID:gid]; + + int z = pos.x + pos.y * layerSize_.width; + + AtlasSprite *tile = [AtlasSprite spriteWithRect:rect spriteManager:self]; +// [tile setOpacity:layerInfo->opacity]; +// [tile setVisible:layerInfo->visible]; + + tile.anchorPoint = CGPointZero; + [self addChild:tile z:z tag:z]; + + [tile setPosition: [self positionAt:pos]]; + + return tile; +} + +-(void) removeTileAt:(CGPoint)pos +{ + int z = pos.x + pos.y * layerSize_.width; + [self removeChildByTag:z cleanup:YES]; +} + +#pragma mark TMXLayer - obtaining positions + +-(CGPoint) positionAt:(CGPoint)pos +{ + CGPoint ret = CGPointZero; + switch( layerOrientation_ ) { + case TMXOrientationOrtho: + ret = [self positionForOrthoAt:pos]; + break; + case TMXOrientationIso: + ret = [self positionForIsoAt:pos]; + break; + case TMXOrientationHex: + ret = [self positionForHexAt:pos]; + break; + } + return ret; +} + +-(CGPoint) positionForOrthoAt:(CGPoint)pos +{ + int x = pos.x * mapTileSize_.width + 0.49f; + int y = (layerSize_.height - pos.y - 1) * mapTileSize_.height + 0.49f; + return ccp(x,y); +} + +-(CGPoint) positionForIsoAt:(CGPoint)pos +{ + int x = mapTileSize_.width /2 * ( layerSize_.width + pos.x - pos.y - 1) + 0.49f; + int y = mapTileSize_.height /2 * (( layerSize_.height * 2 - pos.x - pos.y) - 2) + 0.49f; + +// int x = mapTileSize_.width /2 *(pos.x - pos.y) + 0.49f; +// int y = (layerSize_.height - (pos.x + pos.y) - 1) * mapTileSize_.height/2 + 0.49f; + return ccp(x, y); +} + +-(CGPoint) positionForHexAt:(CGPoint)pos +{ + float diffY = 0; + if( (int)pos.x % 2 == 1 ) + diffY = -mapTileSize_.height/2 ; + + int x = pos.x * mapTileSize_.width*3/4 + 0.49f; + int y = (layerSize_.height - pos.y - 1) * mapTileSize_.height + diffY + 0.49f; + return ccp(x,y); +} +@end + diff --git a/cocos2d/TMXXMLParser.h b/cocos2d/TMXXMLParser.h new file mode 100644 index 0000000..5c82cdf --- /dev/null +++ b/cocos2d/TMXXMLParser.h @@ -0,0 +1,123 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + * 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. + * + */ + + +enum { + TMXLayerAttribNone = 1 << 0, + TMXLayerAttribBase64 = 1 << 1, + TMXLayerAttribGzip = 1 << 2, +}; + +/* TMXLayerInfo 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 TMXLayerInfo : NSObject +{ +@public + NSString *name; + CGSize layerSize; + unsigned int *tiles; + BOOL visible; + unsigned char opacity; + BOOL ownTiles; +} +@end + +/* TMXTilesetInfo 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 TMXTilesetInfo : NSObject +{ +@public + NSString *name; + int firstGid; + CGSize tileSize; + int spacing; + int margin; + + // filename containing the tiles (should be spritesheet / texture atlas) + NSString *sourceImage; + + // size in pixels of the image + CGSize imageSize; +} +-(CGRect) tileForGID:(unsigned int)gid; +@end + +/* TMXMapInfo 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) + + This information is obtained from the TMX file. + + */ +@interface TMXMapInfo : NSObject +{ + + NSMutableString *currentLayer; + NSMutableString *currentString; + BOOL storingCharacters; + int layerAttribs; + +@public + int orientation; + + + // map width & height + CGSize mapSize; + + // tiles width & height + CGSize tileSize; + + // Layers + NSMutableArray *layers; + + // tilesets + NSMutableArray *tilesets; +} + +/** 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/cocos2d/TMXXMLParser.m b/cocos2d/TMXXMLParser.m new file mode 100644 index 0000000..547182e --- /dev/null +++ b/cocos2d/TMXXMLParser.m @@ -0,0 +1,244 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + * TMX Tiled Map support: + * http://www.mapeditor.org + * + */ + +#import +#include + +#import "ccMacros.h" +#import "TMXXMLParser.h" +#import "TMXTiledMap.h" +#import "Support/FileUtils.h" +#import "Support/base64.h" +#import "Support/ZipUtils.h" + +#pragma mark - +#pragma mark TMXLayerInfo + + +@implementation TMXLayerInfo +-(id) init +{ + if( (self=[super init])) { + ownTiles = YES; + } + return self; +} +- (void) dealloc +{ + CCLOG(@"deallocing %@",self); + if( ownTiles && tiles ) { + free( tiles ); + tiles = NULL; + } + [super dealloc]; +} + +@end + +#pragma mark - +#pragma mark TMXTilesetInfo +@implementation TMXTilesetInfo +- (void) dealloc +{ + CCLOG(@"deallocing %@", self); + [sourceImage release]; + [name release]; + [super dealloc]; +} + +-(CGRect) tileForGID:(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 TMXMapInfo + +@implementation TMXMapInfo ++(id) formatWithTMXFile:(NSString*)tmxFile +{ + return [[[self alloc] initWithTMXFile:tmxFile] autorelease]; +} + +-(id) initWithTMXFile:(NSString*)tmxFile +{ + if( (self=[super init])) { + + tilesets = [[NSMutableArray arrayWithCapacity:4] retain]; + layers = [[NSMutableArray arrayWithCapacity:4] retain]; + + // tmp vars + currentString = [[NSMutableString alloc] initWithCapacity:200]; + storingCharacters = NO; + layerAttribs = TMXLayerAttribNone; + + NSURL *url = [NSURL fileURLWithPath:[FileUtils fullPathFromRelativePath:tmxFile]]; + 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]; + + NSError *parseError = [parser parserError]; + if(parseError) { + CCLOG(@"TMXTiledMap: Error parsing TMX file: %@", parseError); + } + + [parser release]; + } + return self; +} +- (void) dealloc +{ + CCLOG(@"deallocing %@", self); + [tilesets release]; + [layers release]; + [currentString release]; + [super dealloc]; +} + +// 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(@"TMXFormat: Unsupported TMX version: %@", version); + NSString *orientationStr = [attributeDict valueForKey:@"orientation"]; + if( [orientationStr isEqualToString:@"orthogonal"]) + orientation = TMXOrientationOrtho; + else if ( [orientationStr isEqualToString:@"isometric"]) + orientation = TMXOrientationIso; + else if( [orientationStr isEqualToString:@"hexagonal"]) + orientation = TMXOrientationHex; + else + CCLOG(@"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]; + + } else if([elementName isEqualToString:@"tileset"]) { + TMXTilesetInfo *tileset = [TMXTilesetInfo new]; + tileset->name = [[attributeDict valueForKey:@"name"] retain]; + tileset->firstGid = [[attributeDict valueForKey:@"firstgid"] intValue]; + tileset->spacing = [[attributeDict valueForKey:@"spacing"] intValue]; + tileset->margin = [[attributeDict valueForKey:@"margin"] intValue]; + tileset->tileSize.width = [[attributeDict valueForKey:@"tilewidth"] intValue]; + tileset->tileSize.height = [[attributeDict valueForKey:@"tileheight"] intValue]; + + [tilesets addObject:tileset]; + [tileset release]; + + } else if([elementName isEqualToString:@"layer"]) { + TMXLayerInfo *layer = [TMXLayerInfo new]; + layer->name = [[attributeDict valueForKey:@"name"] retain]; + layer->layerSize.width = [[attributeDict valueForKey:@"width"] intValue]; + layer->layerSize.height = [[attributeDict valueForKey:@"height"] intValue]; + layer->visible = ![[attributeDict valueForKey:@"visible"] isEqualToString:@"0"]; + if( [attributeDict valueForKey:@"opacity"] ){ + layer->opacity = 255 * [[attributeDict valueForKey:@"opacity"] floatValue]; + }else{ + layer->opacity = 255; + } + + [layers addObject:layer]; + [layer release]; + + } else if([elementName isEqualToString:@"image"]) { + + TMXTilesetInfo *tileset = [tilesets lastObject]; + tileset->sourceImage = [[attributeDict valueForKey:@"source"] retain]; + + } 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; + } + } +} + +- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName +{ + int len = 0; + + if([elementName isEqualToString:@"data"] && layerAttribs&TMXLayerAttribBase64) { + storingCharacters = NO; + + TMXLayerInfo *layer = [layers lastObject]; + + unsigned char *buffer; + len = base64Decode((unsigned char*)[currentString UTF8String], [currentString length], &buffer); + if( ! buffer ) { + CCLOG(@"TiledMap: decode data error"); + return; + } + + if( layerAttribs & TMXLayerAttribGzip ) { + unsigned char *deflated; + len = inflateMemory(buffer, len, &deflated); + free( buffer ); + + if( ! deflated ) { + CCLOG(@"TiledMap: inflate data error"); + return; + } + + layer->tiles = (unsigned int*) deflated; + } else + layer->tiles = (unsigned int*) buffer; + + [currentString setString:@""]; + } +} + +- (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(@"Error on XML Parse: %@", [parseError localizedDescription] ); +} + +@end diff --git a/cocos2d/TextureAtlas.h b/cocos2d/TextureAtlas.h new file mode 100644 index 0000000..c37f218 --- /dev/null +++ b/cocos2d/TextureAtlas.h @@ -0,0 +1,117 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "Texture2D.h" +#import "ccTypes.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 an interleaved vertex array list + */ +@interface TextureAtlas : NSObject { + NSUInteger totalQuads_; + NSUInteger capacity_; + ccV3F_C4B_T2F_Quad *quads_; // quads to be rendered + GLushort *indices; + Texture2D *texture_; +} + +/** 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) Texture2D *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. + */ +-(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:(Texture2D *)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. + */ +-(id) initWithTexture:(Texture2D *)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 remove and insert 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 Texture Atlas. + * 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 all the Atlas's Quads + */ +-(void) drawQuads; + +@end diff --git a/cocos2d/TextureAtlas.m b/cocos2d/TextureAtlas.m new file mode 100644 index 0000000..0d761c0 --- /dev/null +++ b/cocos2d/TextureAtlas.m @@ -0,0 +1,272 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +// cocos2d +#import "TextureAtlas.h" +#import "TextureMgr.h" +#import "ccMacros.h" + +// support +#import "Support/Texture2D.h" + +@interface TextureAtlas (Private) +-(void) initIndices; +@end + +//According to some tests GL_TRIANGLE_STRIP is slower, MUCH slower. Probably I'm doing something very wrong +//#define USE_TRIANGLE_STRIP 1 + +@implementation TextureAtlas + +@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:(Texture2D *)tex capacity:(NSUInteger)n +{ + return [[[self alloc] initWithTexture:tex capacity:n] autorelease]; +} + +-(id) initWithFile:(NSString*)file capacity:(NSUInteger)n +{ + // retained in property + Texture2D *tex = [[TextureMgr sharedTextureMgr] addImage:file]; + + return [self initWithTexture:tex capacity:n]; +} + +-(id) initWithTexture:(Texture2D*)tex capacity:(NSUInteger)n +{ + if( (self=[super init]) ) { + + capacity_ = n; + + // retained in property + self.texture = tex; + + quads_ = malloc( sizeof(quads_[0]) * capacity_ ); + indices = malloc( sizeof(indices[0]) * capacity_ * 6 ); + + if( ! ( quads_ && indices) ) { + NSLog(@"TextureAtlas: not enough memory"); + if( quads_ ) + free(quads_); + if( indices ) + free(indices); + return nil; + } + + [self initIndices]; + } + + return self; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | totalQuads = %i>", [self class], self, totalQuads_]; +} + +-(void) dealloc +{ + CCLOG(@"deallocing %@",self); + + free(quads_); + free(indices); + + [texture_ release]; + + [super dealloc]; +} + +-(void) initIndices +{ + for( NSUInteger i=0;i< capacity_;i++) { +#ifdef 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 + } +} + +#pragma mark TextureAtlas - Update, Insert, Move & Remove + +-(void) updateQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger) n +{ + + NSAssert( n >= 0 && n < capacity_, @"updateQuadWithTexture: Invalid index"); + + totalQuads_ = MAX( n+1, totalQuads_); + + quads_[n] = *quad; +} + + +-(void) insertQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger)index +{ + NSAssert( index >= 0 && index < capacity_, @"updateQuadWithTexture: Invalid index"); + + totalQuads_++; + + NSUInteger remaining = (totalQuads_-1) - index; + + // last object doesn't need to be moved + if( remaining ) { + // tex coordinates + memmove( &quads_[index+1],&quads_[index], sizeof(quads_[0]) * remaining ); + } + + quads_[index] = *quad; +} + + +-(void) insertQuadFromIndex:(NSUInteger)oldIndex atIndex:(NSUInteger)newIndex +{ + NSAssert( newIndex >= 0 && newIndex < totalQuads_, @"insertQuadFromIndex:atIndex: Invalid index"); + NSAssert( oldIndex >= 0 && oldIndex < totalQuads_, @"insertQuadFromIndex:atIndex: Invalid index"); + + if( oldIndex == newIndex ) + return; + + NSUInteger howMany = abs( oldIndex - newIndex); + int dst = oldIndex; + int 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; +} + +-(void) removeQuadAtIndex:(NSUInteger) index +{ + NSAssert( index >= 0 && 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_--; +} + +-(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) ) { + NSLog(@"TextureAtlas: 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]; + + return YES; +} + +#pragma mark TextureAtlas - Drawing + +-(void) drawQuads +{ + return [self drawNumberOfQuads: totalQuads_]; +} + +-(void) drawNumberOfQuads: (NSUInteger) n +{ +#define kPointSize sizeof(quads_[0].bl) + glBindTexture(GL_TEXTURE_2D, [texture_ name]); + + int offset = (int)quads_; + + // vertex + int diff = offsetof( ccV3F_C4B_T2F, vertices); + glVertexPointer(3, GL_FLOAT, kPointSize, (void*) (offset + diff) ); + + // color + diff = offsetof( ccV3F_C4B_T2F, colors); + glColorPointer(4, GL_UNSIGNED_BYTE, kPointSize, (void*)(offset + diff)); + + // tex coords + diff = offsetof( ccV3F_C4B_T2F, texCoords); + glTexCoordPointer(2, GL_FLOAT, kPointSize, (void*)(offset + diff)); + +#ifdef USE_TRIANGLE_STRIP + glDrawElements(GL_TRIANGLE_STRIP, n*6, GL_UNSIGNED_SHORT, indices); +#else + glDrawElements(GL_TRIANGLES, n*6, GL_UNSIGNED_SHORT, indices); +#endif +} + +@end diff --git a/cocos2d/TextureMgr.h b/cocos2d/TextureMgr.h new file mode 100644 index 0000000..d6b4426 --- /dev/null +++ b/cocos2d/TextureMgr.h @@ -0,0 +1,99 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import + +#import "Support/Texture2D.h" + +/** 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 TextureMgr : NSObject +{ + NSMutableDictionary *textures; + NSLock *dictLock; + NSLock *contextLock; +} + +/** Retruns ths shared instance of the Texture Manager */ ++ (TextureMgr *) sharedTextureMgr; + +/** Returns a Texture2D object given an file image + * If the file image was not previously loaded, it will create a new Texture2D + * object and it will return it. + * Otherwise it will return a reference of a previosly loaded image. + * Supported image extensions: .png, .bmp, .tiff, .jpeg, .pvr, .gif + */ +-(Texture2D*) addImage: (NSString*) fileimage; + +/** Returns a Texture2D object given an file image + * If the file image was not previously loaded, it will create a new Texture2D 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 PVRTC RAW filename + * If the file image was not previously loaded, it will create a new Texture2D + * 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 + */ +-(Texture2D*) addPVRTCImage: (NSString*) fileimage bpp:(int)bpp hasAlpha:(BOOL)alpha width:(int)w; + +/** Returns a Texture2D object given an PVRTC filename + * If the file image was not previously loaded, it will create a new Texture2D + * object and it will return it. Otherwise it will return a reference of a previosly loaded image + */ +-(Texture2D*) addPVRTCImage: (NSString*) filename; + +/** Returns a Texture2D object given an CGImageRef image + * If the image was not previously loaded, it will create a new Texture2D object and it will return it. + * Otherwise it will return a reference of a previously loaded image. + * The CGImageRef (a memory pointer) will be used as the "key" for the cache. + * @deprecated Use addCGImage:forKey: instead + */ +-(Texture2D*) addCGImage: (CGImageRef) image __attribute__((deprecated)); +/** Returns a Texture2D object given an CGImageRef image + * If the image was not previously loaded, it will create a new Texture2D 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. + * @since v0.8 + */ +-(Texture2D*) addCGImage: (CGImageRef) image forKey: (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 Texture Manager + */ +-(void) removeTexture: (Texture2D*) tex; +@end diff --git a/cocos2d/TextureMgr.m b/cocos2d/TextureMgr.m new file mode 100644 index 0000000..2295514 --- /dev/null +++ b/cocos2d/TextureMgr.m @@ -0,0 +1,292 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "TextureMgr.h" +#import "ccMacros.h" +#import "Director.h" +#import "Support/FileUtils.h" +#import "Support/Texture2D.h" + +static EAGLContext *auxEAGLcontext = nil; + +@interface AsyncObject : NSObject +{ + SEL selector_; + id target_; + id data_; +} +@property (readwrite,assign) SEL selector; +@property (readwrite,retain) id target; +@property (readwrite,retain) id data; +@end + +@implementation AsyncObject +@synthesize selector = selector_; +@synthesize target = target_; +@synthesize data = data_; +- (void) dealloc +{ + CCLOG(@"deallocing %@", self); + [target_ release]; + [data_ release]; + [super dealloc]; +} + +@end + + + +@implementation TextureMgr + +#pragma mark TextureMgr - Alloc, Init & Dealloc +static TextureMgr *sharedTextureMgr; + ++ (TextureMgr *)sharedTextureMgr +{ + @synchronized([TextureMgr class]) + { + if (!sharedTextureMgr) + [[TextureMgr alloc] init]; + + return sharedTextureMgr; + } + // to avoid compiler warning + return nil; +} + ++(id)alloc +{ + @synchronized([TextureMgr class]) + { + NSAssert(sharedTextureMgr == nil, @"Attempted to allocate a second instance of a singleton."); + sharedTextureMgr = [super alloc]; + return sharedTextureMgr; + } + // to avoid compiler warning + return nil; +} + +-(id) init +{ + if( (self=[super init]) ) { + textures = [[NSMutableDictionary dictionaryWithCapacity: 10] retain]; + dictLock = [[NSLock alloc] init]; + contextLock = [[NSLock alloc] init]; + } + + return self; +} + +-(void) dealloc +{ + CCLOG( @"deallocing %@", self); + + [textures release]; + [dictLock release]; + [contextLock release]; + [auxEAGLcontext release]; + auxEAGLcontext = nil; + sharedTextureMgr = nil; + [super dealloc]; +} + +#pragma mark TextureMgr - Add Images + +-(void) addImageWithAsyncObject:(AsyncObject*)async +{ + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + + // 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( auxEAGLcontext == nil ) { + auxEAGLcontext = [[EAGLContext alloc] + initWithAPI:kEAGLRenderingAPIOpenGLES1 + sharegroup:[[[[Director sharedDirector] openGLView] context] sharegroup]]; + + if( ! auxEAGLcontext ) + CCLOG(@"TextureMgr: Could not create EAGL context"); + } + + if( [EAGLContext setCurrentContext:auxEAGLcontext] ) { + + // load / create the texture + Texture2D *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(@"TetureMgr: EAGLContext error"); + } + [contextLock unlock]; + + [autoreleasepool release]; +} + +-(void) addImageAsync: (NSString*) filename target:(id)target selector:(SEL)selector +{ + NSAssert(filename != nil, @"TextureMgr: fileimage MUST not be nill"); + + // optimization + + Texture2D * tex; + + if( (tex=[textures objectForKey: filename] ) ) { + [target performSelector:selector withObject:tex]; + return; + } + + // schedule the load + + AsyncObject *asyncObject = [[AsyncObject alloc] init]; + asyncObject.selector = selector; + asyncObject.target = target; + asyncObject.data = filename; + + [NSThread detachNewThreadSelector:@selector(addImageWithAsyncObject:) toTarget:self withObject:asyncObject]; + [asyncObject release]; +} + +-(Texture2D*) addImage: (NSString*) path +{ + NSAssert(path != nil, @"TextureMgr: fileimage MUST not be nill"); + + Texture2D * tex = nil; + + // MUTEX: + // Needed since addImageAsync calls this method from a different thread + [dictLock lock]; + + tex=[textures objectForKey: path]; + + if( ! tex ) { + + // Split up directory and filename + NSString *fullpath = [FileUtils fullPathFromRelativePath: path ]; + + // all images are handled by UIImage except PVR extension that is handled by our own handler + if ( [[path lowercaseString] hasSuffix:@".pvr"] ) + tex = [self addPVRTCImage:fullpath]; + else { + + tex = [ [Texture2D alloc] initWithImage: [UIImage imageWithContentsOfFile: fullpath ] ]; + + [textures setObject: tex forKey:path]; + + [tex release]; + } + } + + [dictLock unlock]; + + return tex; +} + +-(Texture2D*) addPVRTCImage: (NSString*) path bpp:(int)bpp hasAlpha:(BOOL)alpha width:(int)w +{ + NSAssert(path != nil, @"TextureMgr: fileimage MUST not be nill"); + NSAssert( bpp==2 || bpp==4, @"TextureMgr: bpp must be either 2 or 4"); + + Texture2D * tex; + + if( (tex=[textures objectForKey: path] ) ) { + return tex; + } + + // Split up directory and filename + NSString *fullpath = [FileUtils fullPathFromRelativePath:path]; + + NSData *nsdata = [[NSData alloc] initWithContentsOfFile:fullpath]; + tex = [[Texture2D alloc] initWithPVRTCData:[nsdata bytes] level:0 bpp:bpp hasAlpha:alpha length:w]; + [textures setObject: tex forKey:path]; + [nsdata release]; + + return [tex autorelease]; +} + +-(Texture2D*) addPVRTCImage: (NSString*) fileimage +{ + NSAssert(fileimage != nil, @"TextureMgr: fileimage MUST not be nill"); + + Texture2D * tex; + + if( (tex=[textures objectForKey: fileimage] ) ) { + return tex; + } + + tex = [[Texture2D alloc] initWithPVRTCFile: fileimage]; + if( tex ) + [textures setObject: tex forKey:fileimage]; + + return [tex autorelease]; +} + +-(Texture2D*) addCGImage: (CGImageRef) image +{ + NSAssert(image != nil, @"TextureMgr: image MUST not be nill"); + + NSString *key = [NSString stringWithFormat:@"%08X",(unsigned long)image]; + + return [self addCGImage: image forKey: key]; +} + +-(Texture2D*) addCGImage: (CGImageRef) image forKey: (NSString *)key +{ + NSAssert(image != nil, @"TextureMgr: image MUST not be nill"); + + Texture2D * tex; + + if( (tex=[textures objectForKey: key] ) ) { + return tex; + } + + tex = [[Texture2D alloc] initWithImage: [UIImage imageWithCGImage:image]]; + [textures setObject: tex forKey:key]; + + return [tex autorelease]; +} + +#pragma mark TextureMgr - Cache + +-(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(@"removing texture: %@", key); + [textures removeObjectForKey:key]; + } + } +} + +-(void) removeTexture: (Texture2D*) tex +{ + if( ! tex ) + return; + + NSArray *keys = [textures allKeysForObject:tex]; + + for( NSUInteger i = 0; i < [keys count]; i++ ) + [textures removeObjectForKey:[keys objectAtIndex:i]]; +} +@end diff --git a/cocos2d/TextureNode.h b/cocos2d/TextureNode.h new file mode 100644 index 0000000..885dd64 --- /dev/null +++ b/cocos2d/TextureNode.h @@ -0,0 +1,57 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import + +#import "Support/Texture2D.h" + +#import "CocosNode.h" + + +/** TextureNode is a subclass of CocosNode that implements the CocosNodeRGBA + * and CocosNodeTexture protocol. + * + * As the name implies it, it knows how to render a textures. + * + * All features from CocosNode are valid, plus the following new features: + * - opacity and RGB + * - texture (can be Aliased or AntiAliased) + */ +@interface TextureNode : CocosNode { + + // texture + Texture2D *texture_; + + // blend func + ccBlendFunc blendFunc_; + + // texture RGBA + GLubyte opacity_; + ccColor3B color_; + BOOL opacityModifyRGB_; + +} + +/** conforms to CocosNodeTexture protocol */ +@property (nonatomic,readwrite,retain) Texture2D *texture; + +/** conforms to CocosNodeTexture protocol */ +@property (nonatomic,readwrite) ccBlendFunc blendFunc; + +/** conforms to CocosNodeRGBA protocol */ +@property (nonatomic,readonly) GLubyte opacity; +/** conforms to CocosNodeRGBA protocol */ +@property (nonatomic,readwrite) ccColor3B color; +@end diff --git a/cocos2d/TextureNode.m b/cocos2d/TextureNode.m new file mode 100644 index 0000000..1f1380e --- /dev/null +++ b/cocos2d/TextureNode.m @@ -0,0 +1,117 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import +#import +#import + +#import "TextureMgr.h" +#import "TextureNode.h" +#import "ccMacros.h" +#import "Support/CGPointExtension.h" + +@implementation TextureNode + +@synthesize opacity=opacity_; +@synthesize color=color_; +@synthesize blendFunc = blendFunc_; + +- (id) init +{ + if( (self=[super init]) ) { + opacity_ = 255; + color_ = ccWHITE; + anchorPoint_ = ccp(0.5f, 0.5f); + blendFunc_.src = CC_BLEND_SRC; + blendFunc_.dst = CC_BLEND_DST; + } + + return self; +} + +-(void) dealloc +{ + [texture_ release]; + [super dealloc]; +} + +-(void) setTexture:(Texture2D*) texture +{ + [texture_ release]; + texture_ = [texture retain]; + [self setContentSize: texture.contentSize]; + if( ! [texture hasPremultipliedAlpha] ) { + blendFunc_.src = GL_SRC_ALPHA; + blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; + } + opacityModifyRGB_ = [texture hasPremultipliedAlpha]; +} + +-(Texture2D*) texture +{ + return texture_; +} + +#pragma mark TextureNode - RGBA protocol +-(void) setRGB: (GLubyte)r :(GLubyte)g :(GLubyte)b +{ + [self setColor:ccc3(r,g,b)]; +} + +-(void) setOpacity:(GLubyte)opacity +{ + // special opacity for premultiplied textures + opacity_ = opacity; + if( opacityModifyRGB_ ) + color_.r = color_.g = color_.b = opacity_; +} +-(void) setOpacityModifyRGB:(BOOL)modify +{ + opacityModifyRGB_ = modify; +} +-(BOOL) doesOpacityModifyRGB +{ + return opacityModifyRGB_; +} + +#pragma mark TextureNode - draw +- (void) draw +{ + glEnableClientState( GL_VERTEX_ARRAY); + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + + glEnable( GL_TEXTURE_2D); + + glColor4ub( color_.r, color_.g, color_.b, opacity_); + + BOOL newBlend = NO; + if( blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST ) { + newBlend = YES; + glBlendFunc( blendFunc_.src, blendFunc_.dst ); + } + + [texture_ drawAtPoint: CGPointZero]; + + if( newBlend ) + glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + + // is this chepear than saving/restoring color state ? + glColor4ub( 255, 255, 255, 255); + + glDisable( GL_TEXTURE_2D); + + glDisableClientState(GL_VERTEX_ARRAY ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); +} +@end diff --git a/cocos2d/TileMapAtlas.h b/cocos2d/TileMapAtlas.h new file mode 100644 index 0000000..3a72d7d --- /dev/null +++ b/cocos2d/TileMapAtlas.h @@ -0,0 +1,70 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "TextureAtlas.h" +#import "AtlasNode.h" +#import "Support/TGAlib.h" + +/** TileMapAtlas is a subclass of AtlasNode. + + 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://blog.sapusmedia.com/2008/12/how-to-use-tilemap-editor-for-cocos2d.html + + All features from AtlasNode are valid in TileMapAtlas + + 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: TMXTiledMap + */ +@interface TileMapAtlas : AtlasNode { + + /// 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 the TileMap with a tile file (atlas) with a map file and the width and height of each tile. + The tile file will be loaded using the TextureMgr. + */ ++(id) tileMapAtlasWithTileFile:(NSString*)tile mapFile:(NSString*)map tileWidth:(int)w tileHeight:(int)h; + +/** initializes the TileMap with a tile file (atlas) with a map file and the width and height of each tile. + 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/cocos2d/TileMapAtlas.m b/cocos2d/TileMapAtlas.m new file mode 100644 index 0000000..7862c17 --- /dev/null +++ b/cocos2d/TileMapAtlas.m @@ -0,0 +1,204 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "TileMapAtlas.h" +#import "ccMacros.h" +#import "Support/FileUtils.h" + +@interface TileMapAtlas (Private) +-(void) loadTGAfile:(NSString*)file; +-(void) calculateItemsToRender; +-(void) updateAtlasValueAt:(ccGridSize)pos withValue:(ccColor3B)value withIndex:(int)idx; +@end + + +@implementation TileMapAtlas + +@synthesize tgaInfo; + +#pragma mark TileMapAtlas - 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 = [FileUtils fullPathFromRelativePath:file ]; + +// //Find the path of the file +// NSBundle *mainBndl = [NSBundle mainBundle]; +// 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 TileMapAtlas - 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(@"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:(int)idx +{ + ccV3F_C4B_T2F_Quad quad; + + int x = pos.x; + int y = pos.y; + float row = (value.r % itemsPerRow) * texStepX; + float col = (value.r / itemsPerRow) * texStepY; + + quad.tl.texCoords.u = row; + quad.tl.texCoords.v = col; + quad.tr.texCoords.u = row + texStepX; + quad.tr.texCoords.v = col; + quad.bl.texCoords.u = row; + quad.bl.texCoords.v = col + texStepY; + quad.br.texCoords.u = row + texStepX; + quad.br.texCoords.v = col + texStepY; + + 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/cocos2d/TiledGridAction.h b/cocos2d/TiledGridAction.h new file mode 100644 index 0000000..3a58fb4 --- /dev/null +++ b/cocos2d/TiledGridAction.h @@ -0,0 +1,198 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 On-Core + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "GridAction.h" + +/** ShakyTiles3D action */ +@interface ShakyTiles3D : TiledGrid3DAction +{ + 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 + +//////////////////////////////////////////////////////////// + +/** ShatteredTiles3D action */ +@interface ShatteredTiles3D : TiledGrid3DAction +{ + 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 + +//////////////////////////////////////////////////////////// + +/** ShuffleTiles action + Shuffle the tiles in random order + */ +@interface ShuffleTiles : TiledGrid3DAction +{ + int seed; + int 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 + +//////////////////////////////////////////////////////////// + +/** FadeOutTRTiles action + Fades out the tiles in a Top-Right direction + */ +@interface FadeOutTRTiles : TiledGrid3DAction +{ +} +@end + +//////////////////////////////////////////////////////////// + +/** FadeOutBLTiles action. + Fades out the tiles in a Bottom-Left direction + */ +@interface FadeOutBLTiles : FadeOutTRTiles +{ +} +@end + +//////////////////////////////////////////////////////////// + +/** FadeOutUpTiles action. + Fades out the tiles in upwards direction + */ +@interface FadeOutUpTiles : FadeOutTRTiles +{ +} +@end + +//////////////////////////////////////////////////////////// + +/** FadeOutDownTiles action. + Fades out the tiles in downwards direction + */ +@interface FadeOutDownTiles : FadeOutUpTiles +{ +} +@end + +//////////////////////////////////////////////////////////// + +/** TurnOffTiles action. + Turn off the files in random order + */ +@interface TurnOffTiles : TiledGrid3DAction +{ + int seed; + int 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 + +//////////////////////////////////////////////////////////// + +/** WavesTiles3D action. */ +@interface WavesTiles3D : TiledGrid3DAction +{ + 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 + +//////////////////////////////////////////////////////////// + +/** JumpTiles3D action. + A sin function is executed to move the tiles across the Z axis + */ +@interface JumpTiles3D : TiledGrid3DAction +{ + 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 + +//////////////////////////////////////////////////////////// + +/** SplitRows action */ +@interface SplitRows : TiledGrid3DAction +{ + CGSize winSize; +} +/** creates the action with the number of rows to split and the duration */ ++(id)actionWithRows:(int)r duration:(ccTime)d; +/** initializes the action with the number of rows to split and the duration */ +-(id)initWithRows:(int)r duration:(ccTime)d; + +@end + +//////////////////////////////////////////////////////////// + +/** SplitCols action */ +@interface SplitCols : TiledGrid3DAction +{ + CGSize winSize; +} +/** creates the action with the number of columns to split and the duration */ ++(id)actionWithCols:(int)c duration:(ccTime)d; +/** initializes the action with the number of columns to split and the duration */ +-(id)initWithCols:(int)c duration:(ccTime)d; + +@end diff --git a/cocos2d/TiledGridAction.m b/cocos2d/TiledGridAction.m new file mode 100644 index 0000000..afc7142 --- /dev/null +++ b/cocos2d/TiledGridAction.m @@ -0,0 +1,663 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 On-Core + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "TiledGridAction.h" +#import "Director.h" +#import "ccMacros.h" +#import "Support/CGPointExtension.h" + +typedef struct +{ + CGPoint position; + CGPoint startPosition; + ccGridSize delta; +} Tile; + + +@implementation ShakyTiles3D + ++(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; +} + +-(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 + +//////////////////////////////////////////////////////////// + +@implementation ShatteredTiles3D + ++(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; +} + +-(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 + +//////////////////////////////////////////////////////////// + +@implementation ShuffleTiles + ++(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; +} + +-(void)dealloc +{ + if ( tilesOrder ) free(tilesOrder); + if ( tiles ) free(tiles); + [super dealloc]; +} + +-(void)shuffle:(int*)array count:(int)len +{ + int i; + for( i = len - 1; i >= 0; i-- ) + { + int j = rand() % (i+1); + int v = array[i]; + array[i] = array[j]; + array[j] = v; + } +} + +-(ccGridSize)getDelta:(ccGridSize)pos +{ + CGPoint pos2; + + int 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)start +{ + [super start]; + + 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 + +//////////////////////////////////////////////////////////// + +@implementation FadeOutTRTiles + +-(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 + +//////////////////////////////////////////////////////////// + +@implementation FadeOutBLTiles + +-(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 + +//////////////////////////////////////////////////////////// + +@implementation FadeOutUpTiles + +-(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 + +//////////////////////////////////////////////////////////// + +@implementation FadeOutDownTiles + +-(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 + +//////////////////////////////////////////////////////////// + +@implementation TurnOffTiles + ++(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; +} + +-(void)dealloc +{ + if ( tilesOrder ) free(tilesOrder); + [super dealloc]; +} + +-(void)shuffle:(int*)array count:(int)len +{ + int i; + for( i = len - 1; i >= 0; i-- ) + { + int 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)start +{ + int i; + + [super start]; + + 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 + +//////////////////////////////////////////////////////////// + +@implementation WavesTiles3D + +@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 = 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 + +//////////////////////////////////////////////////////////// + +@implementation JumpTiles3D + +@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; +} + +-(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 + +//////////////////////////////////////////////////////////// + +@implementation SplitRows + ++(id)actionWithRows:(int)r duration:(ccTime)d +{ + return [[[self alloc] initWithRows:r duration:d] autorelease]; +} + +-(id)initWithRows:(int)r duration:(ccTime)d +{ + return [super initWithSize:ccg(1,r) duration:d]; +} + +-(void)start +{ + [super start]; + winSize = [[Director sharedDirector] winSize]; +} + +-(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 + +//////////////////////////////////////////////////////////// + +@implementation SplitCols + ++(id)actionWithCols:(int)c duration:(ccTime)d +{ + return [[[self alloc] initWithCols:c duration:d] autorelease]; +} + +-(id)initWithCols:(int)c duration:(ccTime)d +{ + return [super initWithSize:ccg(c,1) duration:d]; +} + +-(void)start +{ + [super start]; + winSize = [[Director sharedDirector] winSize]; +} + +-(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/cocos2d/TouchDelegateProtocol.h b/cocos2d/TouchDelegateProtocol.h new file mode 100644 index 0000000..ee083c4 --- /dev/null +++ b/cocos2d/TouchDelegateProtocol.h @@ -0,0 +1,65 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Valentin Milea + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ +#import + +/** + TargetedTouchDelegate. + + 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 TargetedTouchDelegate + +/** 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 + +/** + StandardTouchDelegate. + Each event that is received will be propagated to the delegate, + unless a previous delegate consumes the event. + To consume the event (prevent propagation) the delegate should return kEventHandled. + To ignore the event (the event will be forwarded to the next delegate in the chain) the delegate should return kEventIgnored. + @since v0.8 +*/ +@protocol StandardTouchDelegate +@optional +- (BOOL)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; +- (BOOL)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; +- (BOOL)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; +- (BOOL)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; +@end + +enum { + /// return kEventHandled if the event should NOT be forwarded to the next handler in the chain + kEventHandled = YES, + /// return kEventIgnored if the event should be forwarded to the next handler in the chain + kEventIgnored = NO, +}; diff --git a/cocos2d/TouchDispatcher.h b/cocos2d/TouchDispatcher.h new file mode 100644 index 0000000..61ad0f2 --- /dev/null +++ b/cocos2d/TouchDispatcher.h @@ -0,0 +1,56 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Valentin Milea + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "TouchDelegateProtocol.h" +#import "Support/EAGLView.h" + + +/** TouchDispatcher. + Singleton that handles all the touch events. + The dispatcher dispatches events to the registered TouchHandlers. + @since v0.8 + */ +@interface TouchDispatcher : NSObject +{ + NSMutableArray *touchHandlers; + BOOL dispatchEvents; +} + +/** singleton of the TouchDispatcher */ ++ (TouchDispatcher*)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 diff --git a/cocos2d/TouchDispatcher.m b/cocos2d/TouchDispatcher.m new file mode 100644 index 0000000..e01d9d0 --- /dev/null +++ b/cocos2d/TouchDispatcher.m @@ -0,0 +1,213 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Valentin Milea + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "TouchDispatcher.h" +#import "TouchHandler.h" + +@implementation TouchDispatcher + +@synthesize dispatchEvents; + +static TouchDispatcher *sharedDispatcher = nil; + ++(TouchDispatcher*) sharedDispatcher +{ + @synchronized(self) { + if (sharedDispatcher == nil) + [[self alloc] init]; // assignment not done here + } + return sharedDispatcher; +} + ++(id) allocWithZone:(NSZone *)zone +{ + @synchronized(self) { + if (sharedDispatcher == nil) { + sharedDispatcher = [super allocWithZone:zone]; + return sharedDispatcher; // assignment and return on first allocation + } + } + return nil; // on subsequent allocation attempts return nil +} + +-(id) copyWithZone:(NSZone *)zone { return self; } +-(id) retain { return self; } +-(unsigned) retainCount { return UINT_MAX; } +-(void) release { } +-(id) autorelease { return self; } + +-(id) init +{ + if((self = [super init])) { + + dispatchEvents = YES; + touchHandlers = [[NSMutableArray alloc] initWithCapacity:8]; + } + + return self; +} + +-(void) dealloc +{ + [touchHandlers release]; + [super dealloc]; +} + +// +// handlers management +// + +#pragma mark Adding handlers + +-(void) addHandler:(TouchHandler*) handler +{ + NSUInteger i = 0; + for( TouchHandler *h in touchHandlers ) { + if( h.priority < handler.priority ) + i++; + + if( h.delegate == handler.delegate ) + [NSException raise:NSInvalidArgumentException format:@"Delegate already added to touch dispatcher."]; + } + [touchHandlers insertObject:handler atIndex:i]; +} + +-(void) addStandardDelegate:(id) delegate priority:(int)priority +{ + TouchHandler *handler = [StandardTouchHandler handlerWithDelegate:delegate priority:priority]; + [self addHandler:handler]; +} + +-(void) addTargetedDelegate:(id) delegate priority:(int)priority swallowsTouches:(BOOL)swallowsTouches +{ + TouchHandler *handler = [TargetedTouchHandler handlerWithDelegate:delegate priority:priority swallowsTouches:swallowsTouches]; + [self addHandler:handler]; +} + +#pragma mark Removing handlers + +-(void) removeDelegate:(id) delegate +{ + if( delegate == nil ) + return; + + for( TouchHandler *handler in touchHandlers ) { + if( handler.delegate == delegate ) { + [touchHandlers removeObject:handler]; + break; + } + } +} + +-(void) removeAllDelegates +{ + [touchHandlers removeAllObjects]; +} + +#pragma mark Changing priority of added handlers + +-(void) setPriority:(int) priority forDelegate:(id) delegate +{ + if( delegate == nil ) + [NSException raise:NSInvalidArgumentException format:@"Got nil touch delegate"]; + + TouchHandler *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)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + if( dispatchEvents ) { + NSArray *handlers = [touchHandlers copy]; + NSMutableSet *mutableTouches = [touches mutableCopy]; + + for( TouchHandler *handler in handlers ) { + if( [handler ccTouchesBegan:mutableTouches withEvent:event] == kEventHandled ) + break; + if([mutableTouches count] == 0) + break; + } + [handlers release]; + [mutableTouches release]; + } +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + if( dispatchEvents ) { + NSArray *handlers = [touchHandlers copy]; + NSMutableSet *mutableTouches = [touches mutableCopy]; + + for( TouchHandler *handler in handlers ) { + if( [handler ccTouchesMoved:mutableTouches withEvent:event] == kEventHandled ) + break; + if([mutableTouches count] == 0) + break; + } + [handlers release]; + [mutableTouches release]; + } +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + if( dispatchEvents ) { + NSArray *handlers = [touchHandlers copy]; + NSMutableSet *mutableTouches = [touches mutableCopy]; + + for( TouchHandler *handler in handlers ) { + if( [handler ccTouchesEnded:mutableTouches withEvent:event] == kEventHandled ) + break; + if([mutableTouches count] == 0) + break; + } + [handlers release]; + [mutableTouches release]; + } +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + if( dispatchEvents ) { + NSArray *handlers = [touchHandlers copy]; + NSMutableSet *mutableTouches = [touches mutableCopy]; + + for( TouchHandler *handler in handlers ) { + if( [handler ccTouchesCancelled:mutableTouches withEvent:event] == kEventHandled ) + break; + if([mutableTouches count] == 0) + break; + } + [handlers release]; + [mutableTouches release]; + } +} +@end diff --git a/cocos2d/TouchHandler.h b/cocos2d/TouchHandler.h new file mode 100644 index 0000000..0d9d29a --- /dev/null +++ b/cocos2d/TouchHandler.h @@ -0,0 +1,70 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Valentin Milea + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "TouchDelegateProtocol.h" + +/** + TouchHandler + Object than contains the delegate and priority of the event handler. +*/ +@interface TouchHandler : NSObject { + id delegate; + int priority; +} + +/** delegate */ +@property(nonatomic, readwrite, retain) id delegate; +/** priority */ +@property(nonatomic, readwrite) int priority; // default 0 + +/** 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; + +- (BOOL)ccTouchesBegan:(NSMutableSet *)touches withEvent:(UIEvent *)event; +- (BOOL)ccTouchesMoved:(NSMutableSet *)touches withEvent:(UIEvent *)event; +- (BOOL)ccTouchesEnded:(NSMutableSet *)touches withEvent:(UIEvent *)event; +- (BOOL)ccTouchesCancelled:(NSMutableSet *)touches withEvent:(UIEvent *)event; +@end + +/** StandardTouchHandler + It forwardes each event to the delegate until one delegate returns kEventHandled. + */ +@interface StandardTouchHandler : TouchHandler +{ +} +@end + +/** + TargetedTouchHandler + Object than contains the claimed touches and if it swallos touches. + Used internally by TouchDispatcher + */ +@interface TargetedTouchHandler : TouchHandler { + 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 + diff --git a/cocos2d/TouchHandler.m b/cocos2d/TouchHandler.m new file mode 100644 index 0000000..8ce971b --- /dev/null +++ b/cocos2d/TouchHandler.m @@ -0,0 +1,181 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Valentin Milea + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "TouchHandler.h" +#import "ccMacros.h" + +#pragma mark - +#pragma mark TouchHandler +@implementation TouchHandler + +@synthesize delegate, priority; + ++ (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; + } + + return self; +} + +- (void)dealloc { + CCLOG(@"deallocing %@", self); + [delegate release]; + [super dealloc]; +} + +- (BOOL)ccTouchesBegan:(NSMutableSet *)touches withEvent:(UIEvent *)event +{ + NSAssert(NO, @"override"); + return YES; +} +- (BOOL)ccTouchesMoved:(NSMutableSet *)touches withEvent:(UIEvent *)event +{ + NSAssert(NO, @"override"); + return YES; +} +- (BOOL)ccTouchesEnded:(NSMutableSet *)touches withEvent:(UIEvent *)event +{ + NSAssert(NO, @"override"); + return YES; +} +- (BOOL)ccTouchesCancelled:(NSMutableSet *)touches withEvent:(UIEvent *)event +{ + NSAssert(NO, @"override"); + return YES; +} +@end + +#pragma mark - +#pragma mark StandardTouchHandler +@implementation StandardTouchHandler +- (BOOL)ccTouchesBegan:(NSMutableSet *)touches withEvent:(UIEvent *)event +{ + if( [delegate respondsToSelector:@selector(ccTouchesBegan:withEvent:)] ) + return [delegate ccTouchesBegan:touches withEvent:event]; + return kEventIgnored; +} +- (BOOL)ccTouchesMoved:(NSMutableSet *)touches withEvent:(UIEvent *)event +{ + if( [delegate respondsToSelector:@selector(ccTouchesMoved:withEvent:)] ) + return [delegate ccTouchesMoved:touches withEvent:event]; + return kEventIgnored; +} +- (BOOL)ccTouchesEnded:(NSMutableSet *)touches withEvent:(UIEvent *)event +{ + if( [delegate respondsToSelector:@selector(ccTouchesEnded:withEvent:)] ) + return [delegate ccTouchesEnded:touches withEvent:event]; + return kEventIgnored; +} +- (BOOL)ccTouchesCancelled:(NSMutableSet *)touches withEvent:(UIEvent *)event +{ + if( [delegate respondsToSelector:@selector(ccTouchesCancelled:withEvent:)] ) + return [delegate ccTouchesCancelled:touches withEvent:event]; + return kEventIgnored; +} +@end + +#pragma mark - +#pragma mark TargetedTouchHandler + +@interface TargetedTouchHandler (private) +-(void) updateKnownTouches:(NSMutableSet *)touches withEvent:(UIEvent *)event selector:(SEL)selector unclaim:(BOOL)doUnclaim; +@end + +@implementation TargetedTouchHandler + +@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; + } + + return self; +} + +- (void)dealloc { + [claimedTouches release]; + [super dealloc]; +} + +- (BOOL)ccTouchesBegan:(NSMutableSet *)touches withEvent:(UIEvent *)event +{ + NSMutableSet *copyTouches = [touches copy]; + for( UITouch *touch in copyTouches) { + BOOL touchWasClaimed = [delegate ccTouchBegan:touch withEvent:event]; + + if( touchWasClaimed ) { + [claimedTouches addObject:touch]; + + if( swallowsTouches ) + [touches removeObject:touch]; + } + } + [copyTouches release]; + return kEventIgnored; +} +- (BOOL)ccTouchesMoved:(NSMutableSet *)touches withEvent:(UIEvent *)event +{ + [self updateKnownTouches:touches withEvent:event selector:@selector(ccTouchMoved:withEvent:) unclaim:NO]; + return kEventIgnored; + +} +- (BOOL)ccTouchesEnded:(NSMutableSet *)touches withEvent:(UIEvent *)event +{ + [self updateKnownTouches:touches withEvent:event selector:@selector(ccTouchEnded:withEvent:) unclaim:YES]; + return kEventIgnored; + +} +- (BOOL)ccTouchesCancelled:(NSMutableSet *)touches withEvent:(UIEvent *)event +{ + [self updateKnownTouches:touches withEvent:event selector:@selector(ccTouchCancelled:withEvent:) unclaim:YES]; + return kEventIgnored; +} + +-(void) updateKnownTouches:(NSMutableSet *)touches withEvent:(UIEvent *)event selector:(SEL)selector unclaim:(BOOL)doUnclaim +{ + NSMutableSet *copyTouches = [touches copy]; + for( UITouch *touch in copyTouches) { + if( [claimedTouches containsObject:touch] ) { + + if( [delegate respondsToSelector:selector] ) + [delegate performSelector:selector withObject:touch withObject:event]; + + if( doUnclaim ) + [claimedTouches removeObject:touch]; + + if( swallowsTouches ) + [touches removeObject:touch]; + } + } + [copyTouches release]; +} +@end diff --git a/cocos2d/Transition.h b/cocos2d/Transition.h new file mode 100644 index 0000000..2f9cad1 --- /dev/null +++ b/cocos2d/Transition.h @@ -0,0 +1,262 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "Scene.h" +@class IntervalAction; +@class CocosNode; + +/** 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 Transition scenes + */ +@interface TransitionScene : Scene { + Scene *inScene; + Scene *outScene; + ccTime duration; + BOOL inSceneOnTop; +} +/** creates a base transition with duration and incoming scene */ ++(id) transitionWithDuration:(ccTime) t scene:(Scene*)s; +/** initializes a transition with duration and incoming scene */ +-(id) initWithDuration:(ccTime) t scene:(Scene*)s; +/** called after the transition finishes */ +-(void) finish; +/** used by some transitions to hide the outter scene */ +-(void) hideOutShowIn; +@end + +/** A Transition that supports orientation like. + * Possible orientation: LeftOver, RightOver, UpOver, DownOver + */ +@interface OrientedTransitionScene : TransitionScene +{ + tOrientation orientation; +} +/** creates a base transition with duration and incoming scene */ ++(id) transitionWithDuration:(ccTime) t scene:(Scene*)s orientation:(tOrientation)o; +/** initializes a transition with duration and incoming scene */ +-(id) initWithDuration:(ccTime) t scene:(Scene*)s orientation:(tOrientation)o; +@end + + +/** RotoZoom Transition. + Rotate and zoom out the outgoing scene, and then rotate and zoom in the incoming + */ +@interface RotoZoomTransition : TransitionScene +{} +@end + +/** JumpZoom Transition. + Zoom out and jump the outgoing scene, and then jump and zoom in the incoming +*/ +@interface JumpZoomTransition : TransitionScene +{} +@end + +/** MoveInL Transition. + Move in from to the left the incoming scene. +*/ +@interface MoveInLTransition : TransitionScene +{} +/** initializes the scenes */ +-(void) initScenes; +/** returns the action that will be performed */ +-(IntervalAction*) action; +@end + +/** MoveInR Transition. + Move in from to the right the incoming scene. + */ +@interface MoveInRTransition : MoveInLTransition +{} +@end + +/** MoveInT Transition. + Move in from to the top the incoming scene. + */ +@interface MoveInTTransition : MoveInLTransition +{} +@end + +/** MoveInB Transition. + Move in from to the bottom the incoming scene. + */ +@interface MoveInBTransition : MoveInLTransition +{} +@end + +/** SlideInL Transition. + Slide in the incoming scene from the left border. + */ +@interface SlideInLTransition : TransitionScene +{} +/** initializes the scenes */ +-(void) initScenes; +/** returns the action that will be performed by the incomming and outgoing scene */ +-(IntervalAction*) action; +@end + +/** SlideInR Transition. + Slide in the incoming scene from the right border. + */ +@interface SlideInRTransition : SlideInLTransition +{} +@end + +/** SlideInB Transition. + Slide in the incoming scene from the bottom border. + */ +@interface SlideInBTransition : SlideInLTransition +{} +@end + +/** SlideInT Transition. + Slide in the incoming scene from the top border. + */ +@interface SlideInTTransition : SlideInLTransition +{} +@end + +/** + Shrink the outgoing scene while grow the incoming scene + */ +@interface ShrinkGrowTransition : TransitionScene +{} +@end + +/** FlipX Transition. + Flips the screen horizontally. + The front face is the outgoing scene and the back face is the incoming scene. + */ +@interface FlipXTransition : OrientedTransitionScene +{} +@end + +/** FlipY Transition. + Flips the screen vertically. + The front face is the outgoing scene and the back face is the incoming scene. + */ +@interface FlipYTransition : OrientedTransitionScene +{} +@end + +/** FlipAngular Transition. + Flips the screen half horizontally and half vertically. + The front face is the outgoing scene and the back face is the incoming scene. + */ +@interface FlipAngularTransition : OrientedTransitionScene +{} +@end + +/** ZoomFlipX Transition. + 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 ZoomFlipXTransition : OrientedTransitionScene +{ +} +@end + +/** ZoomFlipY Transition. + 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 ZoomFlipYTransition : OrientedTransitionScene +{} +@end + +/** ZoomFlipAngular Transition. + 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 ZoomFlipAngularTransition : OrientedTransitionScene +{} +@end + +/** Fade Transition. + Fade out the outgoing scene and then fade in the incoming scene.''' + */ +@interface FadeTransition : TransitionScene +{ + 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:(Scene*)scene withColor:(ccColor3B)color; +/** initializes the transition with a duration and with an RGB color */ +-(id) initWithDuration:(ccTime)duration scene:(Scene*)scene withColor:(ccColor3B)color; +@end + +/** TurnOffTiles Transition. + Turn off the tiles of the outgoing scene in random order + */ +@interface TurnOffTilesTransition : TransitionScene +{} +@end + +/** SplitCols Transition. + The odd columns goes upwards while the even columns goes downwards. + */ +@interface SplitColsTransition : TransitionScene +{} +-(IntervalAction*) action; +@end + +/** SplitRows Transition. + The odd rows goes to the left while the even rows goes to the right. + */ +@interface SplitRowsTransition : SplitColsTransition +{} +@end + +/** FadeTRTransition. + Fade the tiles of the outgoing scene from the left-bottom corner the to top-right corner. + */ +@interface FadeTRTransition : TransitionScene +{} +-(IntervalAction*) actionWithSize:(ccGridSize) vector; +@end + +/** FadeBLTransition. + Fade the tiles of the outgoing scene from the top-right corner to the bottom-left corner. + */ +@interface FadeBLTransition : FadeTRTransition +{} +@end + +/** FadeUp Transition. + * Fade the tiles of the outgoing scene from the bottom to the top. + */ +@interface FadeUpTransition : FadeTRTransition +{} +@end + +/** FadeDown Transition. + * Fade the tiles of the outgoing scene from the top to the bottom. + */ +@interface FadeDownTransition : FadeTRTransition +{} +@end diff --git a/cocos2d/Transition.m b/cocos2d/Transition.m new file mode 100644 index 0000000..291151e --- /dev/null +++ b/cocos2d/Transition.m @@ -0,0 +1,908 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#import "Transition.h" +#import "CocosNode.h" +#import "Director.h" +#import "IntervalAction.h" +#import "InstantAction.h" +#import "CameraAction.h" +#import "Layer.h" +#import "Camera.h" +#import "TiledGridAction.h" +#import "EaseAction.h" +#import "TouchDispatcher.h" +#import "Support/CGPointExtension.h" + +enum { + kSceneFade = 0xFADEFADE, +}; + +@interface TransitionScene (Private) +-(void) sceneOrder; +@end + +@implementation TransitionScene ++(id) transitionWithDuration:(ccTime) t scene:(Scene*)s +{ + return [[[self alloc] initWithDuration:t scene:s] autorelease]; +} + +-(id) initWithDuration:(ccTime) t scene:(Scene*)s +{ + NSAssert( s != nil, @"Argument scene must be non-nil"); + + if( (self=[super init]) ) { + + duration = t; + + // retain + inScene = [s retain]; + outScene = [[Director sharedDirector] runningScene]; + [outScene retain]; + + if( inScene == outScene ) { + NSException* myException = [NSException + exceptionWithName:@"TransitionWithInvalidScene" + reason:@"Incoming scene must be different from the outgoing scene" + userInfo:nil]; + @throw myException; + } + + // disable events while transitions + [[TouchDispatcher sharedDispatcher] setDispatchEvents: NO]; + + [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]; + + [[Director sharedDirector] replaceScene: inScene]; + + // enable events while transitions + [[TouchDispatcher sharedDispatcher] setDispatchEvents: YES]; + + // 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]; +} + +-(void) onEnterTransitionDidFinish +{ + [super onEnterTransitionDidFinish]; +} + +-(void) dealloc +{ + [inScene release]; + [outScene release]; + [super dealloc]; +} +@end + +// +// Oriented Transition +// +@implementation OrientedTransitionScene ++(id) transitionWithDuration:(ccTime) t scene:(Scene*)s orientation:(tOrientation)o +{ + return [[[self alloc] initWithDuration:t scene:s orientation:o] autorelease]; +} + +-(id) initWithDuration:(ccTime) t scene:(Scene*)s orientation:(tOrientation)o +{ + if( (self=[super initWithDuration:t scene:s]) ) + orientation = o; + return self; +} +@end + + +// +// RotoZoom +// +@implementation RotoZoomTransition +-(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)]; + + IntervalAction *rotozoom = [Sequence actions: [Spawn actions: + [ScaleBy actionWithDuration:duration/2 scale:0.001f], + [RotateBy actionWithDuration:duration/2 angle:360 *2], + nil], + [DelayTime actionWithDuration:duration/2], + nil]; + + + [outScene runAction: rotozoom]; + [inScene runAction: [Sequence actions: + [rotozoom reverse], + [CallFunc actionWithTarget:self selector:@selector(finish)], + nil]]; +} +@end + +// +// JumpZoom +// +@implementation JumpZoomTransition +-(void) onEnter +{ + [super onEnter]; + CGSize s = [[Director 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)]; + + IntervalAction *jump = [JumpBy actionWithDuration:duration/4 position:ccp(-s.width,0) height:s.width/4 jumps:2]; + IntervalAction *scaleIn = [ScaleTo actionWithDuration:duration/4 scale:1.0f]; + IntervalAction *scaleOut = [ScaleTo actionWithDuration:duration/4 scale:0.5f]; + + IntervalAction *jumpZoomOut = [Sequence actions: scaleOut, jump, nil]; + IntervalAction *jumpZoomIn = [Sequence actions: jump, scaleIn, nil]; + + IntervalAction *delay = [DelayTime actionWithDuration:duration/2]; + + [outScene runAction: jumpZoomOut]; + [inScene runAction: [Sequence actions: delay, + jumpZoomIn, + [CallFunc actionWithTarget:self selector:@selector(finish)], + nil] ]; +} +@end + +// +// MoveInL +// +@implementation MoveInLTransition +-(void) onEnter +{ + [super onEnter]; + + [self initScenes]; + + IntervalAction *a = [self action]; + + [inScene runAction: [Sequence actions: + [EaseOut actionWithAction:a rate:2.0f], + [CallFunc actionWithTarget:self selector:@selector(finish)], + nil] ]; + +} +-(IntervalAction*) action +{ + return [MoveTo actionWithDuration:duration position:ccp(0,0)]; +} + +-(void) initScenes +{ + CGSize s = [[Director sharedDirector] winSize]; + [inScene setPosition: ccp( -s.width,0) ]; +} +@end + +// +// MoveInR +// +@implementation MoveInRTransition +-(void) initScenes +{ + CGSize s = [[Director sharedDirector] winSize]; + [inScene setPosition: ccp( s.width,0) ]; +} +@end + +// +// MoveInT +// +@implementation MoveInTTransition +-(void) initScenes +{ + CGSize s = [[Director sharedDirector] winSize]; + [inScene setPosition: ccp( 0, s.height) ]; +} +@end + +// +// MoveInB +// +@implementation MoveInBTransition +-(void) initScenes +{ + CGSize s = [[Director 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 SlideInLTransition +-(void) onEnter +{ + [super onEnter]; + + [self initScenes]; + + IntervalAction *in = [self action]; + IntervalAction *out = [self action]; + + id inAction = [EaseOut actionWithAction:in rate:2.0f]; + id outAction = [Sequence actions: + [EaseOut actionWithAction:out rate:2.0f], + [CallFunc actionWithTarget:self selector:@selector(finish)], + nil]; + + [inScene runAction: inAction]; + [outScene runAction: outAction]; +} +-(void) sceneOrder +{ + inSceneOnTop = NO; +} +-(void) initScenes +{ + CGSize s = [[Director sharedDirector] winSize]; + [inScene setPosition: ccp( -(s.width-ADJUST_FACTOR),0) ]; +} +-(IntervalAction*) action +{ + CGSize s = [[Director sharedDirector] winSize]; + return [MoveBy actionWithDuration:duration position:ccp(s.width-ADJUST_FACTOR,0)]; +} + +@end + +// +// SlideInR +// +@implementation SlideInRTransition +-(void) sceneOrder +{ + inSceneOnTop = YES; +} +-(void) initScenes +{ + CGSize s = [[Director sharedDirector] winSize]; + [inScene setPosition: ccp( s.width-ADJUST_FACTOR,0) ]; +} + +-(IntervalAction*) action +{ + CGSize s = [[Director sharedDirector] winSize]; + return [MoveBy actionWithDuration:duration position:ccp(-(s.width-ADJUST_FACTOR),0)]; +} + +@end + +// +// SlideInT +// +@implementation SlideInTTransition +-(void) sceneOrder +{ + inSceneOnTop = NO; +} +-(void) initScenes +{ + CGSize s = [[Director sharedDirector] winSize]; + [inScene setPosition: ccp(0,s.height-ADJUST_FACTOR) ]; +} + +-(IntervalAction*) action +{ + CGSize s = [[Director sharedDirector] winSize]; + return [MoveBy actionWithDuration:duration position:ccp(0,-(s.height-ADJUST_FACTOR))]; +} + +@end + +// +// SlideInB +// +@implementation SlideInBTransition +-(void) sceneOrder +{ + inSceneOnTop = YES; +} + +-(void) initScenes +{ + CGSize s = [[Director sharedDirector] winSize]; + [inScene setPosition: ccp(0,-(s.height-ADJUST_FACTOR)) ]; +} + +-(IntervalAction*) action +{ + CGSize s = [[Director sharedDirector] winSize]; + return [MoveBy actionWithDuration:duration position:ccp(0,s.height-ADJUST_FACTOR)]; +} +@end + +// +// ShrinkGrow Transition +// +@implementation ShrinkGrowTransition +-(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)]; + + IntervalAction *scaleOut = [ScaleTo actionWithDuration:duration scale:0.01f]; + IntervalAction *scaleIn = [ScaleTo actionWithDuration:duration scale:1.0f]; + + [inScene runAction: [EaseOut actionWithAction:scaleIn rate:2.0f]]; + [outScene runAction: [Sequence actions: + [EaseOut actionWithAction:scaleOut rate:2.0f], + [CallFunc actionWithTarget:self selector:@selector(finish)], + nil] ]; +} +@end + +// +// FlipX Transition +// +@implementation FlipXTransition +-(void) onEnter +{ + [super onEnter]; + + IntervalAction *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 = [Sequence actions: + [DelayTime actionWithDuration:duration/2], + [Show action], + [OrbitCamera actionWithDuration: duration/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:0 deltaAngleX:0], + [CallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + outA = [Sequence actions: + [OrbitCamera actionWithDuration: duration/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:0 deltaAngleX:0], + [Hide action], + [DelayTime actionWithDuration:duration/2], + nil ]; + + [inScene runAction: inA]; + [outScene runAction: outA]; + +} +@end + +// +// FlipY Transition +// +@implementation FlipYTransition +-(void) onEnter +{ + [super onEnter]; + + IntervalAction *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 = [Sequence actions: + [DelayTime actionWithDuration:duration/2], + [Show action], + [OrbitCamera actionWithDuration: duration/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:90 deltaAngleX:0], + [CallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + outA = [Sequence actions: + [OrbitCamera actionWithDuration: duration/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:90 deltaAngleX:0], + [Hide action], + [DelayTime actionWithDuration:duration/2], + nil ]; + + [inScene runAction: inA]; + [outScene runAction: outA]; + +} +@end + +// +// FlipAngular Transition +// +@implementation FlipAngularTransition +-(void) onEnter +{ + [super onEnter]; + + IntervalAction *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 = [Sequence actions: + [DelayTime actionWithDuration:duration/2], + [Show action], + [OrbitCamera actionWithDuration: duration/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:-45 deltaAngleX:0], + [CallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + outA = [Sequence actions: + [OrbitCamera actionWithDuration: duration/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:45 deltaAngleX:0], + [Hide action], + [DelayTime actionWithDuration:duration/2], + nil ]; + + [inScene runAction: inA]; + [outScene runAction: outA]; +} +@end + +// +// ZoomFlipX Transition +// +@implementation ZoomFlipXTransition +-(void) onEnter +{ + [super onEnter]; + + IntervalAction *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 = [Sequence actions: + [DelayTime actionWithDuration:duration/2], + [Spawn actions: + [OrbitCamera actionWithDuration: duration/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:0 deltaAngleX:0], + [ScaleTo actionWithDuration:duration/2 scale:1], + [Show action], + nil], + [CallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + outA = [Sequence actions: + [Spawn actions: + [OrbitCamera actionWithDuration: duration/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:0 deltaAngleX:0], + [ScaleTo actionWithDuration:duration/2 scale:0.5f], + nil], + [Hide action], + [DelayTime actionWithDuration:duration/2], + nil ]; + + inScene.scale = 0.5f; + [inScene runAction: inA]; + [outScene runAction: outA]; +} +@end + +// +// ZoomFlipY Transition +// +@implementation ZoomFlipYTransition +-(void) onEnter +{ + [super onEnter]; + + IntervalAction *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 = [Sequence actions: + [DelayTime actionWithDuration:duration/2], + [Spawn actions: + [OrbitCamera actionWithDuration: duration/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:90 deltaAngleX:0], + [ScaleTo actionWithDuration:duration/2 scale:1], + [Show action], + nil], + [CallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + outA = [Sequence actions: + [Spawn actions: + [OrbitCamera actionWithDuration: duration/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:90 deltaAngleX:0], + [ScaleTo actionWithDuration:duration/2 scale:0.5f], + nil], + [Hide action], + [DelayTime actionWithDuration:duration/2], + nil ]; + + inScene.scale = 0.5f; + [inScene runAction: inA]; + [outScene runAction: outA]; +} +@end + +// +// ZoomFlipAngular Transition +// +@implementation ZoomFlipAngularTransition +-(void) onEnter +{ + [super onEnter]; + + IntervalAction *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 = [Sequence actions: + [DelayTime actionWithDuration:duration/2], + [Spawn actions: + [OrbitCamera actionWithDuration: duration/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:-45 deltaAngleX:0], + [ScaleTo actionWithDuration:duration/2 scale:1], + [Show action], + nil], + [Show action], + [CallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + outA = [Sequence actions: + [Spawn actions: + [OrbitCamera actionWithDuration: duration/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:45 deltaAngleX:0], + [ScaleTo actionWithDuration:duration/2 scale:0.5f], + nil], + [Hide action], + [DelayTime actionWithDuration:duration/2], + nil ]; + + inScene.scale = 0.5f; + [inScene runAction: inA]; + [outScene runAction: outA]; +} +@end + + +// +// Fade Transition +// +@implementation FadeTransition ++(id) transitionWithDuration:(ccTime)d scene:(Scene*)s withColor:(ccColor3B)color +{ + return [[[self alloc] initWithDuration:d scene:s withColor:color] autorelease]; +} + +-(id) initWithDuration:(ccTime)d scene:(Scene*)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:(Scene*)s +{ + return [self initWithDuration:d scene:s withColor:ccBLACK]; +} + +-(void) onEnter +{ + [super onEnter]; + + ColorLayer *l = [ColorLayer layerWithColor:color]; + [inScene setVisible: NO]; + + [self addChild: l z:2 tag:kSceneFade]; + + + CocosNode *f = [self getChildByTag:kSceneFade]; + + IntervalAction *a = [Sequence actions: + [FadeIn actionWithDuration:duration/2], + [CallFunc actionWithTarget:self selector:@selector(hideOutShowIn)], + [FadeOut actionWithDuration:duration/2], + [CallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + [f runAction: a]; +} + +-(void) onExit +{ + [super onExit]; + [self removeChildByTag:kSceneFade cleanup:NO]; +} +@end + +// +// TurnOffTilesTransition +// +@implementation TurnOffTilesTransition + +// override addScenes, and change the order +-(void) sceneOrder +{ + inSceneOnTop = NO; +} + +-(void) onEnter +{ + [super onEnter]; + CGSize s = [[Director sharedDirector] winSize]; + float aspect = s.width / s.height; + int x = 12 * aspect; + int y = 12; + + id toff = [TurnOffTiles actionWithSize: ccg(x,y) duration:duration]; + [outScene runAction: [Sequence actions: toff, + [CallFunc actionWithTarget:self selector:@selector(finish)], + [StopGrid action], + nil] + ]; + +} +@end + +#pragma mark Split Transitions + +// +// SplitCols Transition +// +@implementation SplitColsTransition + +-(void) onEnter +{ + [super onEnter]; + + inScene.visible = NO; + + id split = [self action]; + id seq = [Sequence actions: + split, + [CallFunc actionWithTarget:self selector:@selector(hideOutShowIn)], + [split reverse], + nil + ]; + [self runAction: [Sequence actions: + [EaseInOut actionWithAction:seq rate:3.0f], + [CallFunc actionWithTarget:self selector:@selector(finish)], + [StopGrid action], + nil] + ]; +} + +-(IntervalAction*) action +{ + return [SplitCols actionWithCols:3 duration:duration/2.0f]; +} +@end + +// +// SplitRows Transition +// +@implementation SplitRowsTransition +-(IntervalAction*) action +{ + return [SplitRows actionWithRows:3 duration:duration/2.0f]; +} +@end + + +#pragma mark Fade Grid Transitions + +// +// FadeTR Transition +// +@implementation FadeTRTransition +-(void) sceneOrder +{ + inSceneOnTop = NO; +} + +-(void) onEnter +{ + [super onEnter]; + + CGSize s = [[Director sharedDirector] winSize]; + float aspect = s.width / s.height; + int x = 12 * aspect; + int y = 12; + + id action = [self actionWithSize:ccg(x,y)]; + + [outScene runAction: [Sequence actions: + action, + [CallFunc actionWithTarget:self selector:@selector(finish)], + [StopGrid action], + nil] + ]; +} + +-(IntervalAction*) actionWithSize: (ccGridSize) v +{ + return [FadeOutTRTiles actionWithSize:v duration:duration]; +} +@end + +// +// FadeBL Transition +// +@implementation FadeBLTransition +-(IntervalAction*) actionWithSize: (ccGridSize) v +{ + return [FadeOutBLTiles actionWithSize:v duration:duration]; +} +@end + +// +// FadeUp Transition +// +@implementation FadeUpTransition +-(IntervalAction*) actionWithSize: (ccGridSize) v +{ + return [FadeOutUpTiles actionWithSize:v duration:duration]; +} +@end + +// +// FadeDown Transition +// +@implementation FadeDownTransition +-(IntervalAction*) actionWithSize: (ccGridSize) v +{ + return [FadeOutDownTiles actionWithSize:v duration:duration]; +} +@end + + + diff --git a/cocos2d/ccExceptions.h b/cocos2d/ccExceptions.h new file mode 100644 index 0000000..fec2bb0 --- /dev/null +++ b/cocos2d/ccExceptions.h @@ -0,0 +1,23 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +// cocos2d Exceptions +// based on OC3D OC3DConstants.h file +// + +static NSString *kccException_OpenGLViewAlreadyAttached = @"kccException_OpenGLViewAlreadyAttached"; +static NSString *kccException_OpenGLViewNotAttached = @"kccException_OpenGLViewNotAttached"; +static NSString *kccException_OpenGLViewCantDetach = @"kccException_OpenGLViewCantDetach"; +static NSString *kccException_OpenGLViewCantAttach = @"kccException_OpenGLViewCantAttach"; +static NSString *kccException_OpenGLViewCantInit = @"kccException_OpenGLViewCantInit"; diff --git a/cocos2d/ccMacros.h b/cocos2d/ccMacros.h new file mode 100644 index 0000000..a76db2d --- /dev/null +++ b/cocos2d/ccMacros.h @@ -0,0 +1,55 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import + +/** + @file + cocos2d helper macros + */ + +#ifdef COCOS2D_DEBUG +//#define CCLOG(s, …) NSLog((@”%s %s:%d ” s), __func__, basename(__FILE__), __LINE__, ## __VA_ARGS__); +#define CCLOG(...) NSLog(__VA_ARGS__) +#else +#define CCLOG(...) do {} while (0) +#endif + +//simple macro that swaps 2 variables +#define CC_SWAP( x, y ) \ +({ __typeof__(x) temp = (x); \ + x = y; y = temp; \ +}) + + + + +/// returns a random float between -1 and 1 +#define CCRANDOM_MINUS1_1() ((random() / (float)0x3fffffff )-1.0f) + +/// returns a random float between 0 and 1 +#define CCRANDOM_0_1() ((random() / (float)0x7fffffff )) + +/// converts degrees to radians +#define CC_DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) / 180.0f * (float)M_PI) + +/// converts radians to degrees +#define CC_RADIANS_TO_DEGREES(__ANGLE__) ((__ANGLE__) / (float)M_PI * 180.0f) + +/// default gl blend src function +//#define CC_BLEND_SRC GL_SRC_ALPHA +#define CC_BLEND_SRC GL_ONE +/// default gl blend dst function +#define CC_BLEND_DST GL_ONE_MINUS_SRC_ALPHA + diff --git a/cocos2d/ccTypes.h b/cocos2d/ccTypes.h new file mode 100644 index 0000000..82baafb --- /dev/null +++ b/cocos2d/ccTypes.h @@ -0,0 +1,217 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +/** + @file + cocos2d (cc) types +*/ + +#import // CGPoint +#import // GLenum, GLubyte + +/** 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 +{ + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char 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 { + float r; + float g; + float b; + float a; +} ccColor4F; + +/** A vertex composed of 2 floats: x, y + @since v0.8 + */ +#define ccVertex2F CGPoint + +/** A vertex composed of 2 floats: x, y + @since v0.8 + */ +typedef struct _ccVertex3F +{ + float x; + float y; + float z; +} ccVertex3F; + +/** A texcoord composed of 2 floats: u, y + @since v0.8 + */ +typedef struct _ccTex2F { + float u; + float v; +} ccTex2F; + + +//! Point Sprite component +typedef struct _ccPointSprite +{ + ccVertex2F pos; // 8 bytes + ccColor4F colors; // 16 bytes + float 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 +{ + int x; + int y; +} ccGridSize; + +//! helper function to create a ccGridSize +static inline ccGridSize +ccg(const int x, const int y) +{ + ccGridSize v = {x, y}; + return v; +} + +//! 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 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/cocos2d/cocos2d.h b/cocos2d/cocos2d.h new file mode 100644 index 0000000..b3931cb --- /dev/null +++ b/cocos2d/cocos2d.h @@ -0,0 +1,99 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +/** @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 00 08 01 +#define COCOS2D_VERSION 0x00000801 + +// +// all cocos2d include files +// +#import "Action.h" +#import "Camera.h" +#import "CameraAction.h" +#import "CocosNode.h" +#import "Director.h" +#import "TouchDispatcher.h" +#import "TouchDelegateProtocol.h" +#import "InstantAction.h" +#import "IntervalAction.h" +#import "EaseAction.h" +#import "Label.h" +#import "Layer.h" +#import "Menu.h" +#import "MenuItem.h" +#import "ParticleSystem.h" +#import "PointParticleSystem.h" +#import "QuadParticleSystem.h" +#import "ParticleExamples.h" +#import "DrawingPrimitives.h" +#import "Scene.h" +#import "Scheduler.h" +#import "Sprite.h" +#import "TextureMgr.h" +#import "TextureNode.h" +#import "Transition.h" +#import "TextureAtlas.h" +#import "LabelAtlas.h" +#import "TileMapAtlas.h" +#import "AtlasNode.h" +#import "EaseAction.h" +#import "TiledGridAction.h" +#import "Grabber.h" +#import "Grid.h" +#import "Grid3DAction.h" +#import "GridAction.h" +#import "AtlasSprite.h" +#import "AtlasSpriteManager.h" +#import "BitmapFontAtlas.h" +#import "ParallaxNode.h" +#import "ActionManager.h" +#import "TMXTiledMap.h" +#import "RenderTexture.h" +#import "MotionStreak.h" + +// +// cocos2d macros +// +#import "ccTypes.h" +#import "ccMacros.h" + +// +// cocos2d helper files +// +#import "Support/OpenGL_Internal.h" +#import "Support/Texture2D.h" +#import "Support/EAGLView.h" +#import "Support/FileUtils.h" +#import "Support/CGPointExtension.h" + + +// free functions +NSString * cocos2dVersion(void); diff --git a/cocos2d/cocos2d.m b/cocos2d/cocos2d.m new file mode 100644 index 0000000..b40ce1b --- /dev/null +++ b/cocos2d/cocos2d.m @@ -0,0 +1,22 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import + +static NSString *version = @"cocos2d v0.8.1"; + +NSString *cocos2dVersion() +{ + return version; +} diff --git a/cocoslive/ScoreServerPost.h b/cocoslive/ScoreServerPost.h new file mode 100644 index 0000000..c5c0213 --- /dev/null +++ b/cocoslive/ScoreServerPost.h @@ -0,0 +1,123 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#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 ScoreServerPost : 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; +} + +/** status from the score post */ +@property (nonatomic,readonly) tPostStatus postStatus; + +/** 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 CocosLivePostDelegate +/** 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/cocoslive/ScoreServerPost.m b/cocoslive/ScoreServerPost.m new file mode 100644 index 0000000..61d14e0 --- /dev/null +++ b/cocoslive/ScoreServerPost.m @@ -0,0 +1,318 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import "ScoreServerPost.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 ScoreServerPost (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 ScoreServerPost + +@synthesize postStatus = postStatus_; +@synthesize ranking = ranking_; +@synthesize scoreDidUpdate = scoreDidUpdate_; + ++(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 +{ + CCLOG( @"deallocing %@", self); + [delegate release]; + [gameKey release]; + [gameName release]; + [bodyValues release]; + [receivedData 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 + NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:post delegate:self]; + + if ( ! theConnection) + return NO; + + // XXX: Don't release 'theConnection' here + // XXX: It will be released by the delegate + + 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, and the data object + [connection release]; + + if( [delegate respondsToSelector:@selector(scorePostFail:) ] ) + [delegate scorePostFail:self]; +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + [connection release]; + + NSString *dataString = [NSString stringWithCString:[receivedData bytes] length: [receivedData length]]; +// NSString *dataString = [NSString stringWithCString:[receivedData bytes] encoding: NSUTF8StringEncoding]; + if( [dataString isEqual: @"OK"] ) { + + // Ok + postStatus_ = kPostStatusOK; + + if( [delegate respondsToSelector:@selector(scorePostOk:) ] ) + [delegate scorePostOk:self]; + + } else 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 { + + CCLOG(@"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/cocoslive/ScoreServerRequest.h b/cocoslive/ScoreServerRequest.h new file mode 100644 index 0000000..cc4d81c --- /dev/null +++ b/cocoslive/ScoreServerRequest.h @@ -0,0 +1,103 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +#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 ScoreServerRequest : 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; + +} + +/** 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 + +/** CocosLiveRequest protocol */ +@protocol CocosLiveRequestDelegate +-(void) scoreRequestOk:(id) sender; +-(void) scoreRequestRankOk:(id) sender; +-(void) scoreRequestFail:(id) sender; +@end diff --git a/cocoslive/ScoreServerRequest.m b/cocoslive/ScoreServerRequest.m new file mode 100644 index 0000000..e126910 --- /dev/null +++ b/cocoslive/ScoreServerRequest.m @@ -0,0 +1,245 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +// 3rd party imports +#import "CJSONDeserializer.h" + +// local imports +#import "ScoreServerPost.h" +#import "ScoreServerRequest.h" +#import "ccMacros.h" + +@implementation ScoreServerRequest ++(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 +{ + CCLOG( @"deallocing %@", self); + + [delegate release]; + [gameName release]; + [receivedData 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 + NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:request delegate:self]; + if (! theConnection) + return NO; + + // XXX: Don't release 'theConnection' here + // XXX: It will be released by the delegate + + 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; + NSString *jsonString = [NSString stringWithCString:[receivedData bytes] length: [receivedData length]]; + // NSString *jsonString = [NSString stringWithCString:[receivedData bytes] encoding: NSUTF8StringEncoding]; + + NSData *jsonData = [jsonString dataUsingEncoding:NSUTF32BigEndianStringEncoding]; + NSError *error = nil; + NSDictionary *dictionary = [[CJSONDeserializer deserializer] deserializeAsDictionary:jsonData error:&error]; + + if( ! error ) { + array = [dictionary objectForKey:@"scores"]; + } else { + CCLOG(@"Error parsing scores: %@", error); + CCLOG(@"Data: %@", jsonString); + } + 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 + NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:request delegate:self]; + if (! theConnection) + return NO; + + // XXX: Don't release 'theConnection' here + // XXX: It will be released by the delegate + + return YES; +} + +-(int) parseRank { + NSString *rankStr = [NSString stringWithCString:[receivedData bytes] length: [receivedData length]]; + + // 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 + [connection release]; + + CCLOG(@"Error getting scores: %@", error); + + if( [delegate respondsToSelector:@selector(scoreRequestFail:) ] ) + [delegate scoreRequestFail:self]; + +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + // release the connection, and the data object + [connection release]; + + 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/cocoslive/cocoslive.h b/cocoslive/cocoslive.h new file mode 100644 index 0000000..3f7f056 --- /dev/null +++ b/cocoslive/cocoslive.h @@ -0,0 +1,30 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + + +// 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 "ScoreServerPost.h" +#import "ScoreServerRequest.h" + + +// free functions +NSString * cocos2dVersion(void); diff --git a/cocoslive/cocoslive.m b/cocoslive/cocoslive.m new file mode 100644 index 0000000..a5b231d --- /dev/null +++ b/cocoslive/cocoslive.m @@ -0,0 +1,22 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * Copyright (C) 2008,2009 Ricardo Quesada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 'cocos2d for iPhone' license. + * + * You will find a copy of this license within the cocos2d for iPhone + * distribution inside the "LICENSE" file. + * + */ + +#import + +static NSString *version = @"cocoslive v0.3.0"; + +NSString *cocosLiveVersion() +{ + return version; +} diff --git a/cyan.png b/cyan.png new file mode 100644 index 0000000000000000000000000000000000000000..7d2972b9c49a2aff6ad70386d6bcf1f7c51fd73e GIT binary patch literal 419 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 literal 0 HcmV?d00001 diff --git a/gameover.jpeg b/gameover.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..723e705e06530d1518976faf31cf1a48b2c11e3e GIT binary patch literal 9842 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 C?dVwm literal 0 HcmV?d00001 diff --git a/green.png b/green.png new file mode 100644 index 0000000000000000000000000000000000000000..0e09a8e0458afd75631459e8868f27a05059daa4 GIT binary patch literal 404 zcmV;F0c-w=P)Bn@UCJD(!M~Ngr-WO9eWHx5A zI4$PoTvfiZWIcNut*~S}6U2$DP_6ZPR?LhxYHPL()M|ci|@1>;|mWGEw_;qUO`Ki4U#GlFs@t0oV2!GNe z{tu<~j^rhyNOJofIil>Vf9FU*m8%Z+u+ux?Az~az{_o$T2(>C#wXNa1oX3=g4kV|1 y2dd9C99lg9>P^Ld{1&ZTm#2}Sq2=$m3NQc+t!lAkSgjWT0000#J+tqSGfltiN_vf@uOyUu3eZ|)Y?S0EV#SwXa3qUJtpkANUPf)Nt6tx>QOXd}DW`}D z8juolnTJN6R>AfIazK``ovo?Leh(QcON_JDBJ7YZ0F`tZS|(&Ic89QqcSy1RKqi2W fEY_ASz62Nm{ZXi8oLSAg00000NkvXXu0mjfzmL7w literal 0 HcmV?d00001 diff --git a/main.m b/main.m new file mode 100755 index 0000000..cb76755 --- /dev/null +++ b/main.m @@ -0,0 +1,16 @@ +// +// main.m +// Tetris +// +// Created by Joshua Aburto on 9/26/09. +// Copyright __MyCompanyName__ 2009. All rights reserved. +// + +#import + +int main(int argc, char *argv[]) { + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + int retVal = UIApplicationMain(argc, argv, nil, @"TetrisAppDelegate"); + [pool release]; + return retVal; +} diff --git a/orange.png b/orange.png new file mode 100644 index 0000000000000000000000000000000000000000..ac005f026890d6249a9ab4bb9cc2e743b35cde87 GIT binary patch literal 402 zcmV;D0d4+?P)-TKnz81r4cLso?l|i z0*MtutcqF$g-qv>jOSrIX$67R3{(PPSy@=|$+fNA6<=Q9>>OaEfRezB#^4O{&}(dN zu(?(kHE2X8#BpZjW>CSTvAf6m%2=D-u+xDd&+?weTg|W6^bT zP+4SqEV};GXmcz;|7f%i&_N!5YcxJS)ltF^mt8?P)oA6D!r#6c4u5|ZBOp-Vl-ixP z>fqS`xH_YP)3DDNwv=)z9fIV|D9)Oz8Knx^-jO#xVG?EB4U#VDB>`Du_7V0S#2p|R wDl%yHRHX?6!Nf|fcjLZYmw5=a!kYjC04{7?bEJ|}sQ>@~07*qoM6N<$fgkbla`l zY!tRFS<4~x-FrqXB_a}RI2`PHt&LIfbm~W4M_RLUSsNGA$(f z{a^Y5COV4$tcd@qBE|2_n{_pqVzsF~nxE*ktG5f!`f4PJDS03X={-u+)+oXp0646c z+BP*!IG+QSr|b`rB#zHrO1b^sY3=ifYCM9B4*+fmHm&tDXJyDDs}#{Fr}sz_w*xTw iUaJU3$S1(J00RJ}EI&<14{FZ<0000Iu{VjGkfPfP~5W(72!2#t3@d5GP zq4wYzl9!wybv1}ho1Zb?c^@3g>rxuQ9ugZsLZ}_`H+lwJT-v|aW2p(MM0{RVrOJg>^mztdOZvld(aDVOYLW@C7tXS?6A>tzuWmo zM{=1Rxo_pgSi=_9wmzftgHE#h)9`t3jdfxYSSMtXO6w=B{`V$j%VO532knvHa;{Wc zoCyjcCCfw#icdNEkPs6pH_U$BsEC*mqRlopdOu7UXDijlj?|a3@-hlt@6nC$!QQ#0 iqhnJ?iMz&^00RI{%R7cy`1}L_0000