From d19677f9f73d8fe7436982bdf8d40f69d69d1b43 Mon Sep 17 00:00:00 2001
From: Starla Insigna <starla4444@gmail.com>
Date: Fri, 12 Aug 2011 09:51:50 -0400
Subject: Added tutorial mode

GameLayer was also renamed to ClassicGameMode and its pause functionality and score/lives counters were moved out to GameMode. FallingObjectDelegate was created to allow for custom code to be run when items were caught/missed, something that is used extensively by tutorial mode.

Important: Cart Collect's bundle identifier was changed in this revision from com.fourisland.Cart-Collect to com.apatheticink.Cart-Collect, so ensure that before attempting to compile/run this build, you clear your build directory and remove the old version of Cart Collect from your device.

Closes #193
---
 Classes/Cart.h                    |   2 +
 Classes/Cart.m                    |  10 +-
 Classes/Cart_CollectAppDelegate.m |   4 +-
 Classes/ClassicGameMode.h         |  20 +++
 Classes/ClassicGameMode.m         | 166 ++++++++++++++++++
 Classes/FallingObject.h           |   3 +
 Classes/FallingObject.m           |  25 ++-
 Classes/FallingObjectDelegate.h   |  20 +++
 Classes/GameLayer.h               |  31 ----
 Classes/GameLayer.m               | 284 -------------------------------
 Classes/GameMode.h                |  19 ++-
 Classes/GameMode.m                | 112 +++++++++++-
 Classes/MainMenuLayer.m           |  12 +-
 Classes/TutorialBubble.m          |  13 +-
 Classes/TutorialMode.h            |  26 +++
 Classes/TutorialMode.m            | 349 ++++++++++++++++++++++++++++++++++++++
 16 files changed, 763 insertions(+), 333 deletions(-)
 create mode 100755 Classes/ClassicGameMode.h
 create mode 100755 Classes/ClassicGameMode.m
 create mode 100644 Classes/FallingObjectDelegate.h
 delete mode 100755 Classes/GameLayer.h
 delete mode 100755 Classes/GameLayer.m
 create mode 100644 Classes/TutorialMode.h
 create mode 100644 Classes/TutorialMode.m

(limited to 'Classes')

diff --git a/Classes/Cart.h b/Classes/Cart.h
index d708d73..0ef1abf 100644
--- a/Classes/Cart.h
+++ b/Classes/Cart.h
@@ -12,9 +12,11 @@
 @interface Cart : NSObject {
     CCSprite* sprite;
     float accelX;
+    BOOL immobile;
 }
 
 @property (readonly) CCSprite* sprite;
+@property (assign) BOOL immobile;
 - (id)initWithSprite:(CCSprite*)sprite;
 - (void)tick;
 - (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration;
diff --git a/Classes/Cart.m b/Classes/Cart.m
index 3046be3..45c370c 100644
--- a/Classes/Cart.m
+++ b/Classes/Cart.m
@@ -10,7 +10,7 @@
 
 @implementation Cart
 
-@synthesize sprite;
+@synthesize sprite, immobile;
 
 - (id)initWithSprite:(CCSprite*)m_sprite
 {
@@ -19,6 +19,7 @@
     if (nil != self)
     {
         sprite = m_sprite;
+        immobile = NO;
     }
     
     return self;
@@ -26,8 +27,11 @@
 
 - (void)tick
 {
-    // Move the cart based on acceleration gathered from accelerometer
-	sprite.position = ccp(MIN(MAX(sprite.position.x+accelX, 16),464), sprite.position.y);
+    if (!immobile)
+    {
+        // Move the cart based on acceleration gathered from accelerometer
+        sprite.position = ccp(MIN(MAX(sprite.position.x+accelX, 16),464), sprite.position.y);
+    }
 }
 
 - (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration
diff --git a/Classes/Cart_CollectAppDelegate.m b/Classes/Cart_CollectAppDelegate.m
index 7547601..158f660 100755
--- a/Classes/Cart_CollectAppDelegate.m
+++ b/Classes/Cart_CollectAppDelegate.m
@@ -10,7 +10,7 @@
 
 #import "Cart_CollectAppDelegate.h"
 #import "GameConfig.h"
-#import "GameLayer.h"
+#import "GameMode.h"
 #import "RootViewController.h"
 #import "MainMenuLayer.h"
 
@@ -141,7 +141,7 @@
 	
 	if ([[CCDirector sharedDirector] runningScene].tag == GAME_SCENE)
 	{
-        [((GameLayer*)[[[CCDirector sharedDirector] runningScene] getChildByTag:GAME_LAYER]) pause];
+        [((GameMode*)[[[CCDirector sharedDirector] runningScene] getChildByTag:GAME_LAYER]) pause];
 	}
 }
 
diff --git a/Classes/ClassicGameMode.h b/Classes/ClassicGameMode.h
new file mode 100755
index 0000000..bbce029
--- /dev/null
+++ b/Classes/ClassicGameMode.h
@@ -0,0 +1,20 @@
+//
+//  GameLayer.h
+//  Cart Collect
+//
+//  Created by iD Student Account on 7/18/11.
+//  Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "cocos2d.h"
+#import "GameMode.h"
+
+@interface ClassicGameMode : GameMode {
+	float addSpeed;
+}
+
+- (id)init;
+- (void)randomlyAddObject:(ccTime)dt;
+
+@end
diff --git a/Classes/ClassicGameMode.m b/Classes/ClassicGameMode.m
new file mode 100755
index 0000000..eb766a1
--- /dev/null
+++ b/Classes/ClassicGameMode.m
@@ -0,0 +1,166 @@
+//
+//  GameLayer.m
+//  Cart Collect
+//
+//  Created by iD Student Account on 7/18/11.
+//  Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "ClassicGameMode.h"
+#import "FallingObject.h"
+#import "Cherry.h"
+#import "Bottle.h"
+#import "OneUp.h"
+#import "Rock.h"
+#import "GameOverLayer.h"
+#import "SimpleAudioEngine.h"
+
+@implementation ClassicGameMode
+
+- (void)tick:(ccTime)dt
+{
+	int lastScore = score;
+	
+	[super tick:dt];
+	
+	if (lives == 0)
+	{
+        [self unscheduleAllSelectors];
+
+        [[CCDirector sharedDirector] replaceScene:[CCTransitionSlideInT transitionWithDuration:1.5f scene:[GameOverLayer sceneWithScore:score]]];
+	} else if (score > lastScore)
+	{
+		if ((lastScore < 6500) && (score >= 6500))
+		{
+			[self unschedule:@selector(randomlyAddObject:)];
+			[self schedule:@selector(randomlyAddObject:) interval:0.6f];
+			addSpeed = 0.6f;
+		} else if ((lastScore < 4500) && (score >= 4500))
+		{
+			[self unschedule:@selector(randomlyAddObject:)];
+			[self schedule:@selector(randomlyAddObject:) interval:0.7f];
+			addSpeed = 0.7f;
+		} else if ((lastScore < 2500) && (score >= 2500))
+		{
+			[self unschedule:@selector(randomlyAddObject:)];
+			[self schedule:@selector(randomlyAddObject:) interval:0.8f];
+			addSpeed = 0.8f;
+		} else if ((lastScore < 1500) && (score >= 1500))
+		{
+			[self unschedule:@selector(randomlyAddObject:)];
+			[self schedule:@selector(randomlyAddObject:) interval:0.9f];
+			addSpeed = 0.9f;
+		} else if ((lastScore < 500) && (score >= 500))
+		{
+			[self unschedule:@selector(randomlyAddObject:)];
+			[self schedule:@selector(randomlyAddObject:) interval:1.0f];
+			addSpeed = 1.0f;
+		} else if ((lastScore < 150) && (score >= 150))
+		{
+			[self unschedule:@selector(randomlyAddObject:)];
+			[self schedule:@selector(randomlyAddObject:) interval:2.0f];
+			addSpeed = 2.0f;
+		}
+	}
+}
+
+- (void)randomlyAddObject:(ccTime)dt
+{
+	FallingObject* object;
+    int oneuppercent = 98 - (lives == 1 ? 1 : 0);
+
+	if (score < 1000)
+	{
+		int randomval = arc4random()%100;
+		
+		if (randomval < 65)
+		{
+			object = [[Cherry alloc] init];
+		} else if (randomval < oneuppercent)
+		{
+			object = [[Bottle alloc] init];
+		} else {
+			object = [[OneUp alloc] init];
+		}
+	} else {
+		int randomval = arc4random()%100;
+		
+		if (randomval < 40)
+		{
+			object = [[Cherry alloc] init];
+		} else if (randomval < 70)
+		{
+			object = [[Rock alloc] init];
+		} else if (randomval < oneuppercent)
+		{
+			object = [[Bottle alloc] init];
+		} else {
+			object = [[OneUp alloc] init];
+		}
+	}
+	
+	int objectX = arc4random()%448+16;
+	object.sprite.position = ccp(objectX, 360);
+	object.sprite.scale = 1;
+	[self addChild:object.sprite];
+	
+	[objects addObject:object];
+    [object release];
+	
+	if (score >= 2000)
+	{
+		if (arc4random() % 100 > 80)
+		{
+			object = [[Rock alloc] init];
+			
+			objectX = arc4random()%448+16;
+			object.sprite.position = ccp(objectX, 360);
+			object.sprite.scale = 1;
+			[self addChild:object.sprite];
+			
+			[objects addObject:object];
+            [object release];
+		}
+	}
+	
+	if (score >= 4000)
+	{
+		if (arc4random() % 100 > 80)
+		{
+			object = [[Rock alloc] init];
+			
+			objectX = arc4random()%448+16;
+			object.sprite.position = ccp(objectX, 360);
+			object.sprite.scale = 1;
+			[self addChild:object.sprite];
+			
+			[objects addObject:object];
+            [object release];
+		}
+	}
+}
+
+- (id)init
+{
+	self = [super init];
+	
+	if (nil != self)
+	{
+		CCSprite* backgroundImage = [CCSprite spriteWithFile:@"SeaBeach.png"];
+		backgroundImage.position = ccp(240, 160);
+		[self addChild:backgroundImage z:-1];
+		
+		addSpeed = 2.5f;
+	}
+	
+	return self;
+}
+
+- (void)onEnterTransitionDidFinish
+{
+	[super onEnterTransitionDidFinish];
+	
+    [self schedule:@selector(randomlyAddObject:) interval:addSpeed];
+}
+
+@end
diff --git a/Classes/FallingObject.h b/Classes/FallingObject.h
index 0bda787..3e28903 100755
--- a/Classes/FallingObject.h
+++ b/Classes/FallingObject.h
@@ -8,14 +8,17 @@
 
 #import <Foundation/Foundation.h>
 #import "cocos2d.h"
+#import "FallingObjectDelegate.h"
 
 @interface FallingObject : NSObject {
 	CCSprite* sprite;
     int weight;
+    id<FallingObjectDelegate> delegate;
 }
 
 @property (readonly) CCSprite* sprite;
 @property (readonly) int weight;
+@property (nonatomic,retain) id<FallingObjectDelegate> delegate;
 - (id)init;
 - (BOOL)tick;
 - (void)collideWithCart;
diff --git a/Classes/FallingObject.m b/Classes/FallingObject.m
index 515948e..85ea902 100755
--- a/Classes/FallingObject.m
+++ b/Classes/FallingObject.m
@@ -11,7 +11,7 @@
 
 @implementation FallingObject
 
-@synthesize sprite, weight;
+@synthesize sprite, weight, delegate;
 
 - (id)init
 {
@@ -46,6 +46,16 @@
                 {
                     [self collideWithCart];
                     
+                    if ((delegate != nil) && ([delegate respondsToSelector:@selector(didCatchItem:)]))
+                    {
+                        [delegate didCatchItem:self];
+                    }
+                    
+                    if ((delegate != nil) && ([delegate respondsToSelector:@selector(didDestroyItem:)]))
+                    {
+                        [delegate didDestroyItem:self];
+                    }
+                    
                     return YES;
                 }
             }
@@ -57,6 +67,16 @@
     {
         [self collideWithFloor];
         
+        if ((delegate != nil) && ([delegate respondsToSelector:@selector(didMissItem:)]))
+        {
+            [delegate didMissItem:self];
+        }
+        
+        if ((delegate != nil) && ([delegate respondsToSelector:@selector(didDestroyItem:)]))
+        {
+            [delegate didDestroyItem:self];
+        }
+        
         return YES;
     }
     
@@ -75,7 +95,8 @@
 
 - (void)dealloc
 {
-    [sprite release];
+    [sprite.parent removeChild:sprite cleanup:YES];
+    [delegate release];
     [super dealloc];
 }
 
diff --git a/Classes/FallingObjectDelegate.h b/Classes/FallingObjectDelegate.h
new file mode 100644
index 0000000..b5c03fb
--- /dev/null
+++ b/Classes/FallingObjectDelegate.h
@@ -0,0 +1,20 @@
+//
+//  FallingObjectDelegate.h
+//  Cart Collect
+//
+//  Created by Starla Insigna on 8/10/11.
+//  Copyright 2011 Four Island. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class FallingObject;
+
+@protocol FallingObjectDelegate <NSObject>
+
+@optional
+- (void)didCatchItem:(FallingObject*)item;
+- (void)didMissItem:(FallingObject*)item;
+- (void)didDestroyItem:(FallingObject*)item;
+
+@end
diff --git a/Classes/GameLayer.h b/Classes/GameLayer.h
deleted file mode 100755
index 128cbf2..0000000
--- a/Classes/GameLayer.h
+++ /dev/null
@@ -1,31 +0,0 @@
-//
-//  GameLayer.h
-//  Cart Collect
-//
-//  Created by iD Student Account on 7/18/11.
-//  Copyright 2011 __MyCompanyName__. All rights reserved.
-//
-
-#import <Foundation/Foundation.h>
-#import "cocos2d.h"
-#import "TutorialBubble.h"
-#import "GameMode.h"
-
-@interface GameLayer : GameMode {
-	CCLabelBMFont* scoreLabel;
-	CCLabelBMFont* livesLabel;
-	float addSpeed;
-    TutorialBubble* currentTutorial;
-    
-    CCLayerColor* shadedLayer;
-    CCLayer* pauseLayer;
-}
-
-@property (nonatomic,retain) TutorialBubble* currentTutorial;
-- (id)init;
-- (void)pause;
-- (void)unpause;
-- (void)mainmenu;
-- (void)endTutorial;
-
-@end
diff --git a/Classes/GameLayer.m b/Classes/GameLayer.m
deleted file mode 100755
index 95bc83b..0000000
--- a/Classes/GameLayer.m
+++ /dev/null
@@ -1,284 +0,0 @@
-//
-//  GameLayer.m
-//  Cart Collect
-//
-//  Created by iD Student Account on 7/18/11.
-//  Copyright 2011 __MyCompanyName__. All rights reserved.
-//
-
-#import "GameLayer.h"
-#import "FallingObject.h"
-#import "Cherry.h"
-#import "Bottle.h"
-#import "OneUp.h"
-#import "Rock.h"
-#import "GameOverLayer.h"
-#import "SimpleAudioEngine.h"
-#import "MainMenuLayer.h"
-
-@implementation GameLayer
-
-@synthesize currentTutorial;
-
-- (void)tick:(ccTime)dt
-{
-	int lastScore = score;
-	
-	[super tick:dt];
-	
-	if (lives == 0)
-	{
-        [self unscheduleAllSelectors];
-
-        [[CCDirector sharedDirector] replaceScene:[CCTransitionSlideInT transitionWithDuration:1.5f scene:[GameOverLayer sceneWithScore:score]]];
-	} else if (score > lastScore)
-	{
-		if ((lastScore < 6500) && (score >= 6500))
-		{
-			[self unschedule:@selector(randomlyAddObject:)];
-			[self schedule:@selector(randomlyAddObject:) interval:0.6f];
-			addSpeed = 0.6f;
-		} else if ((lastScore < 4500) && (score >= 4500))
-		{
-			[self unschedule:@selector(randomlyAddObject:)];
-			[self schedule:@selector(randomlyAddObject:) interval:0.7f];
-			addSpeed = 0.7f;
-		} else if ((lastScore < 2500) && (score >= 2500))
-		{
-			[self unschedule:@selector(randomlyAddObject:)];
-			[self schedule:@selector(randomlyAddObject:) interval:0.8f];
-			addSpeed = 0.8f;
-		} else if ((lastScore < 1500) && (score >= 1500))
-		{
-			[self unschedule:@selector(randomlyAddObject:)];
-			[self schedule:@selector(randomlyAddObject:) interval:0.9f];
-			addSpeed = 0.9f;
-		} else if ((lastScore < 500) && (score >= 500))
-		{
-			[self unschedule:@selector(randomlyAddObject:)];
-			[self schedule:@selector(randomlyAddObject:) interval:1.0f];
-			addSpeed = 1.0f;
-		} else if ((lastScore < 150) && (score >= 150))
-		{
-			[self unschedule:@selector(randomlyAddObject:)];
-			[self schedule:@selector(randomlyAddObject:) interval:2.0f];
-			addSpeed = 2.0f;
-		}
-	}
-}
-
-- (void)randomlyAddObject:(ccTime)dt
-{
-	FallingObject* object;
-    int oneuppercent = 98 - (lives == 1 ? 1 : 0);
-
-	if (score < 1000)
-	{
-		int randomval = arc4random()%100;
-		
-		if (randomval < 65)
-		{
-			object = [[Cherry alloc] init];
-		} else if (randomval < oneuppercent)
-		{
-			object = [[Bottle alloc] init];
-		} else {
-			object = [[OneUp alloc] init];
-		}
-	} else {
-		int randomval = arc4random()%100;
-		
-		if (randomval < 40)
-		{
-			object = [[Cherry alloc] init];
-		} else if (randomval < 70)
-		{
-			object = [[Rock alloc] init];
-		} else if (randomval < oneuppercent)
-		{
-			object = [[Bottle alloc] init];
-		} else {
-			object = [[OneUp alloc] init];
-		}
-	}
-	
-	int objectX = arc4random()%448+16;
-	object.sprite.position = ccp(objectX, 360);
-	object.sprite.scale = 1;
-	[self addChild:object.sprite];
-	
-	[objects addObject:object];
-    [object release];
-	
-	if (score >= 2000)
-	{
-		if (arc4random() % 100 > 80)
-		{
-			object = [[Rock alloc] init];
-			
-			objectX = arc4random()%448+16;
-			object.sprite.position = ccp(objectX, 360);
-			object.sprite.scale = 1;
-			[self addChild:object.sprite];
-			
-			[objects addObject:object];
-            [object release];
-		}
-	}
-	
-	if (score >= 4000)
-	{
-		if (arc4random() % 100 > 80)
-		{
-			object = [[Rock alloc] init];
-			
-			objectX = arc4random()%448+16;
-			object.sprite.position = ccp(objectX, 360);
-			object.sprite.scale = 1;
-			[self addChild:object.sprite];
-			
-			[objects addObject:object];
-            [object release];
-		}
-	}
-}
-
-- (id)init
-{
-	self = [super init];
-	
-	int winWidth = [CCDirector sharedDirector].winSize.width;
-	//int winHeight = [CCDirector sharedDirector].winSize.height;
-	int cartScale = 2;
-	
-	if (self != nil)
-	{
-		CCSprite* backgroundImage = [CCSprite spriteWithFile:@"SeaBeach.png"];
-		backgroundImage.position = ccp(240, 160);
-		[self addChild:backgroundImage z:0];
-		
-		cart = [[Cart alloc] initWithSprite:[CCSprite spriteWithFile:@"cart.png"]];
-		cart.sprite.position = ccp(winWidth/2, 22);
-		cart.sprite.scale = cartScale;
-		[self addChild:cart.sprite];
-        
-        scoreLabel = [CCLabelBMFont labelWithString:@"Score: 0" fntFile:@"helvetica2.fnt"];
-		scoreLabel.position = ccp(50, 300);
-		[self addChild:scoreLabel];
-		
-		livesLabel = [CCLabelBMFont labelWithString:@"Lives: 3" fntFile:@"helvetica2.fnt"];
-		livesLabel.position = ccp(50, 280);
-		[self addChild:livesLabel];
-		
-		score = 0;
-		lives = 3;
-		
-		CCMenuItemImage* pauseButton = [CCMenuItemImage itemFromNormalImage:@"pause2.png" selectedImage:@"pause.png" target:self selector:@selector(pause)];
-		CCMenu* pauseMenu = [CCMenu menuWithItems:pauseButton, nil];
-		[pauseMenu setPosition:ccp(480-8-16, 320-8-16)];
-		[self addChild:pauseMenu];
-		
-		addSpeed = 2.5f;
-	}
-	
-	return self;
-}
-
-- (void)onEnter
-{
-	[super onEnter];
-	
-    [self schedule:@selector(randomlyAddObject:) interval:addSpeed];
-}
-
-- (void)setScore:(int)m_score
-{
-    score = m_score;
-    
-    [scoreLabel setString:[NSString stringWithFormat:@"Score: %d", score]];
-}
-
-- (void)setLives:(int)m_lives
-{
-    lives = m_lives;
-    
-    [livesLabel setString:[NSString stringWithFormat:@"Lives: %d", lives]];
-}
-
-- (void)pause
-{
-    if (self.currentTutorial != nil)
-    {
-        [self.currentTutorial removeFromSuperview];
-    }
-    
-    [self pauseSchedulerAndActions];
-    
-    shadedLayer = [CCLayerColor layerWithColor:ccc4(0, 0, 0, 127)];
-	[[[CCDirector sharedDirector] runningScene] addChild:shadedLayer];
-    
-    pauseLayer = [CCLayer node];
-    CCLabelBMFont* scoreLabel2 = [CCLabelBMFont labelWithString:@"PAUSE" fntFile:@"helvetica.fnt"];
-    scoreLabel2.position = ccp(240,90);
-    [pauseLayer addChild:scoreLabel2];
-    
-    CCMenuItemImage* pauseButton = [CCMenuItemImage itemFromNormalImage:@"pause2.png" selectedImage:@"pause.png" target:self selector:@selector(unpause)];
-    CCMenu* pauseMenu = [CCMenu menuWithItems:pauseButton, nil];
-    [pauseMenu setPosition:ccp(480-8-16, 320-8-16)];
-    [pauseLayer addChild:pauseMenu];
-    
-    CCMenuItemImage* newgameMenuItem = [CCMenuItemImage itemFromNormalImage:@"back.png" selectedImage:@"back2.png" target:self selector:@selector(mainmenu)];
-    CCMenu* myMenu = [CCMenu menuWithItems:newgameMenuItem, nil];
-    myMenu.position = ccp(240, 60);
-    [pauseLayer addChild:myMenu];
-    
-    [[[CCDirector sharedDirector] runningScene] addChild:pauseLayer];
-}
-
-- (void)unpause
-{
-    [[[CCDirector sharedDirector] runningScene] removeChild:shadedLayer cleanup:YES];
-    [[[CCDirector sharedDirector] runningScene] removeChild:pauseLayer cleanup:YES];
-    
-    shadedLayer = nil;
-    pauseLayer = nil;
-    
-    if (self.currentTutorial != nil)
-    {
-        [[[CCDirector sharedDirector] openGLView] addSubview:self.currentTutorial];
-    } else {
-        [self resumeSchedulerAndActions];
-    }
-}
-
-- (void)mainmenu
-{
-    [[CCDirector sharedDirector] replaceScene:[MainMenuLayer scene]];
-}
-
-- (void)setCurrentTutorial:(TutorialBubble *)m_currentTutorial
-{
-    @synchronized(self)
-    {
-        if (currentTutorial != m_currentTutorial)
-        {
-            [currentTutorial release];
-            currentTutorial = [m_currentTutorial retain];
-        }
-    }
-    
-    if (currentTutorial != nil)
-    {
-        [currentTutorial setTarget:self action:@selector(endTutorial)];
-        [[[CCDirector sharedDirector] openGLView] addSubview:currentTutorial];
-        [self pauseSchedulerAndActions];
-    }
-}
-
-- (void)endTutorial
-{
-    self.currentTutorial = nil;
-    [self resumeSchedulerAndActions];
-}
-
-@end
diff --git a/Classes/GameMode.h b/Classes/GameMode.h
index bd47c90..370550c 100644
--- a/Classes/GameMode.h
+++ b/Classes/GameMode.h
@@ -17,11 +17,26 @@
 	int score;
 	int lives;
     Cart* cart;
+    
+    CCLayerColor* shadedLayer;
+    CCLayer* pauseLayer;
+    
+    CCLabelBMFont* scoreLabel;
+	CCLabelBMFont* livesLabel;
+    
+    void (^delayedAction)(void);
 }
 
 @property (readonly) Cart* cart;
-@property (assign) int score;
-@property (assign) int lives;
+@property (nonatomic,assign) int score;
+@property (nonatomic,assign) int lives;
++ (CCScene*)scene;
 - (void)tick:(ccTime)dt;
+- (BOOL)canPause;
+- (void)pause;
+- (void)unpause;
+- (void)mainmenu;
+- (void)scheduleDelayedAction:(void(^)(void))delayedAction delay:(float)delay;
+- (void)runDelayedAction;
 
 @end
diff --git a/Classes/GameMode.m b/Classes/GameMode.m
index 6fa31e5..7afbf20 100644
--- a/Classes/GameMode.m
+++ b/Classes/GameMode.m
@@ -8,6 +8,7 @@
 
 #import "GameMode.h"
 #import "FallingObject.h"
+#import "MainMenuLayer.h"
 
 @implementation GameMode
 
@@ -16,7 +17,7 @@
 + (CCScene*)scene
 {
 	CCScene* scene = [CCScene node];
-		
+    
 	GameMode* layer = [self node];
     layer.tag = GAME_LAYER;
 	[scene addChild:layer];
@@ -35,6 +36,30 @@
         isAccelerometerEnabled_ = YES;
         
         objects = [[NSMutableSet alloc] init];
+        
+        cart = [[Cart alloc] initWithSprite:[CCSprite spriteWithFile:@"cart.png"]];
+		cart.sprite.position = ccp(240, 22);
+		cart.sprite.scale = 2;
+		[self addChild:cart.sprite];
+        
+        scoreLabel = [CCLabelBMFont labelWithString:@"Score: 0" fntFile:@"helvetica2.fnt"];
+		scoreLabel.position = ccp(50, 300);
+		[self addChild:scoreLabel];
+		
+		livesLabel = [CCLabelBMFont labelWithString:@"Lives: 3" fntFile:@"helvetica2.fnt"];
+		livesLabel.position = ccp(50, 280);
+		[self addChild:livesLabel];
+		
+		score = 0;
+		lives = 3;
+		
+        if ([self canPause])
+        {
+            CCMenuItemImage* pauseButton = [CCMenuItemImage itemFromNormalImage:@"pause2.png" selectedImage:@"pause.png" target:self selector:@selector(pause)];
+            CCMenu* pauseMenu = [CCMenu menuWithItems:pauseButton, nil];
+            [pauseMenu setPosition:ccp(480-8-16, 320-8-16)];
+            [self addChild:pauseMenu];
+        }
     }
     
     return self;
@@ -45,9 +70,9 @@
 	[cart accelerometer:accelerometer didAccelerate:acceleration];
 }
 
-- (void)onEnter
+- (void)onEnterTransitionDidFinish
 {
-    [super onEnter];
+    [super onEnterTransitionDidFinish];
     
     [[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / 60)];
     [self schedule:@selector(tick:) interval:1.0f/60.0f];
@@ -61,13 +86,90 @@
 	{
         if ([object tick])
         {
-            [object retain];
-            [self removeChild:object.sprite cleanup:YES];
             [objects removeObject:object];
         }
     }
 }
 
+- (BOOL)canPause
+{
+    return YES;
+}
+
+- (void)pause
+{
+    if ([self canPause])
+    {
+        [self pauseSchedulerAndActions];
+        
+        shadedLayer = [CCLayerColor layerWithColor:ccc4(0, 0, 0, 127)];
+        [[[CCDirector sharedDirector] runningScene] addChild:shadedLayer];
+        
+        pauseLayer = [CCLayer node];
+        CCLabelBMFont* scoreLabel2 = [CCLabelBMFont labelWithString:@"PAUSE" fntFile:@"helvetica.fnt"];
+        scoreLabel2.position = ccp(240,90);
+        [pauseLayer addChild:scoreLabel2];
+        
+        CCMenuItemImage* pauseButton = [CCMenuItemImage itemFromNormalImage:@"pause2.png" selectedImage:@"pause.png" target:self selector:@selector(unpause)];
+        CCMenu* pauseMenu = [CCMenu menuWithItems:pauseButton, nil];
+        [pauseMenu setPosition:ccp(480-8-16, 320-8-16)];
+        [pauseLayer addChild:pauseMenu];
+        
+        CCMenuItemImage* newgameMenuItem = [CCMenuItemImage itemFromNormalImage:@"back.png" selectedImage:@"back2.png" target:self selector:@selector(mainmenu)];
+        CCMenu* myMenu = [CCMenu menuWithItems:newgameMenuItem, nil];
+        myMenu.position = ccp(240, 60);
+        [pauseLayer addChild:myMenu];
+        
+        [[[CCDirector sharedDirector] runningScene] addChild:pauseLayer];
+    }
+}
+
+- (void)unpause
+{
+    [[[CCDirector sharedDirector] runningScene] removeChild:shadedLayer cleanup:YES];
+    [[[CCDirector sharedDirector] runningScene] removeChild:pauseLayer cleanup:YES];
+    
+    shadedLayer = nil;
+    pauseLayer = nil;
+    
+    [self resumeSchedulerAndActions];
+}
+
+- (void)mainmenu
+{
+    [[CCDirector sharedDirector] replaceScene:[MainMenuLayer scene]];
+}
+
+- (void)setScore:(int)m_score
+{
+    score = m_score;
+    
+    [scoreLabel setString:[NSString stringWithFormat:@"Score: %d", score]];
+}
+
+- (void)setLives:(int)m_lives
+{
+    lives = m_lives;
+    
+    [livesLabel setString:[NSString stringWithFormat:@"Lives: %d", lives]];
+}
+
+- (void)scheduleDelayedAction:(void(^)(void))m_delayedAction delay:(float)delay
+{
+    delayedAction = Block_copy([m_delayedAction retain]);
+    
+    [self schedule:@selector(runDelayedAction) interval:delay];
+}
+
+- (void)runDelayedAction
+{
+    [self unschedule:@selector(runDelayedAction)];
+    
+    delayedAction();
+    [delayedAction release];
+    delayedAction = nil;
+}
+
 - (void)dealloc
 {
     [objects release];
diff --git a/Classes/MainMenuLayer.m b/Classes/MainMenuLayer.m
index 11cffd7..50c595f 100755
--- a/Classes/MainMenuLayer.m
+++ b/Classes/MainMenuLayer.m
@@ -8,7 +8,8 @@
 
 #import "MainMenuLayer.h"
 #import "HighscoreListController.h"
-#import "GameLayer.h"
+#import "ClassicGameMode.h"
+#import "TutorialMode.h"
 #import "Cart_CollectAppDelegate.h"
 
 @implementation MainMenuLayer
@@ -57,7 +58,14 @@
 
 - (void)newgame
 {
-	[[CCDirector sharedDirector] replaceScene:[GameLayer scene]];
+    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+    
+    if ([defaults boolForKey:@"hasDoneTutorial"])
+    {
+        [[CCDirector sharedDirector] replaceScene:[ClassicGameMode scene]];
+    } else {
+        [[CCDirector sharedDirector] replaceScene:[TutorialMode scene]];
+    }
 }
 
 - (void)highscores
diff --git a/Classes/TutorialBubble.m b/Classes/TutorialBubble.m
index b85aa31..74f667f 100644
--- a/Classes/TutorialBubble.m
+++ b/Classes/TutorialBubble.m
@@ -134,6 +134,17 @@
             boxLoc = CGPointMake(CGRectGetMaxX(spriteBounds), CGRectGetMaxY(spriteBounds) - self.frame.size.height);
             arrowLoc = CGPointMake(0, button.frame.size.height - spriteBounds.size.height/2 + 4);
         }
+    } else {
+        arrowRotation = 180;
+        
+        if (CGRectGetMidX(spriteBounds) < button.frame.size.width)
+        {
+            boxLoc = CGPointMake(0, CGRectGetMaxY(spriteBounds)-4);
+            arrowLoc = CGPointMake(spriteBounds.origin.x+4, 0);
+        } else {
+            boxLoc = CGPointMake(CGRectGetMaxX(spriteBounds) - self.frame.size.width, CGRectGetMaxY(spriteBounds)-4);
+            arrowLoc = CGPointMake(button.frame.size.width - spriteBounds.size.width/2 + 4, 0);
+        }
     }
     
     CGImageRef framestuff = [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"framestuff" ofType:@"png"]] CGImage];
@@ -153,8 +164,6 @@
 
 - (void)buttonPressed:(id)sender
 {
-    [self removeFromSuperview];
-    
     if (target != nil)
     {
         [target performSelector:action];
diff --git a/Classes/TutorialMode.h b/Classes/TutorialMode.h
new file mode 100644
index 0000000..17b0c7f
--- /dev/null
+++ b/Classes/TutorialMode.h
@@ -0,0 +1,26 @@
+//
+//  TutorialMode.h
+//  Cart Collect
+//
+//  Created by Starla Insigna on 8/10/11.
+//  Copyright 2011 Four Island. All rights reserved.
+//
+
+#import "GameMode.h"
+#import "TutorialBubble.h"
+#import "FallingObjectDelegate.h"
+
+@interface TutorialMode : GameMode <FallingObjectDelegate> {
+    TutorialBubble* currentTutorial;
+    BOOL showedDeathBubble;
+    int randomItemsDropped;
+}
+
+@property (nonatomic,retain) TutorialBubble* currentTutorial;
+- (void)endTutorial;
+- (FallingObject*)dropSpecificItem:(FallingObject*)item;
+- (FallingObject*)dropRandomItem;
+- (void)randomlyAddObject:(ccTime)dt;
+- (void)skipTutorial;
+
+@end
diff --git a/Classes/TutorialMode.m b/Classes/TutorialMode.m
new file mode 100644
index 0000000..37e8bd0
--- /dev/null
+++ b/Classes/TutorialMode.m
@@ -0,0 +1,349 @@
+//
+//  TutorialMode.m
+//  Cart Collect
+//
+//  Created by Starla Insigna on 8/10/11.
+//  Copyright 2011 Four Island. All rights reserved.
+//
+
+#import "TutorialMode.h"
+#import "FallingObject.h"
+#import "Cherry.h"
+#import "Bottle.h"
+#import "OneUp.h"
+#import "Rock.h"
+#import "ClassicGameMode.h"
+
+// Item tags:
+//    2000 - first dropped item
+//    2001 - item that is dropped after you catch first dropped item to demonstrate what happens when you miss
+//    2002 - items that are dropped after you miss first dropped item to demonstrate what happens when you catch
+//    2003 - 1-Up
+//    2009 - rock
+
+@implementation TutorialMode
+
+@synthesize currentTutorial;
+
+- (id)init
+{
+    self = [super init];
+    
+    if (nil != self)
+    {
+        CCSprite* backgroundImage = [CCSprite spriteWithFile:@"SeaBeach.png"];
+		backgroundImage.position = ccp(240, 160);
+		[self addChild:backgroundImage z:-1];
+        
+        CCMenuItemImage* menuItem1 = [CCMenuItemImage itemFromNormalImage:@"skiptutorial.png" selectedImage:@"skiptutorial2.png" target:self selector:@selector(skipTutorial)];
+        CCMenu* theMenu = [CCMenu menuWithItems:menuItem1, nil];
+        theMenu.position = ccp(480-16-16-62, 320-8-16);
+        [self addChild:theMenu];
+        
+        showedDeathBubble = NO;
+        randomItemsDropped = 0;
+    }
+    
+    return self;
+}
+
+- (void)onEnterTransitionDidFinish
+{
+    [super onEnterTransitionDidFinish];
+    
+    [self scheduleDelayedAction:^{
+        TutorialBubble* bubble = [[TutorialBubble alloc] initWithText:@"Welcome to Cart Collect. This is a tutorial designed to help you get started playing the game. Below this bubble is a cart. Tilt your device to move it." name:@"cart" spriteReference:cart.sprite];
+        self.currentTutorial = bubble;
+        [bubble release];
+    } delay:2.0f];
+}
+
+- (void)tick:(ccTime)dt
+{
+    [super tick:dt];
+    
+    FallingObject* object = [objects anyObject];
+    if ((object.sprite.tag == 2000) && (object.sprite.position.y == 360-object.weight*14))
+    {
+        TutorialBubble* bubble = [[TutorialBubble alloc] initWithText:@"This is an item. Try to catch it with your cart." name:@"item" spriteReference:object.sprite];
+        self.currentTutorial = bubble;
+        [bubble release];
+    } else if ((object.sprite.tag == 2003) && (object.sprite.position.y == 360-object.weight*8))
+    {
+        TutorialBubble* bubble = [[TutorialBubble alloc] initWithText:@"This is a 1-Up. Catch it to gain an extra life. There's no penalty for not catching it, though." name:@"oneup" spriteReference:object.sprite];
+        self.currentTutorial = bubble;
+        [bubble release];
+    } else if ((object.sprite.tag == 2009) && (object.sprite.position.y == 360-object.weight*14))
+    {
+        TutorialBubble* bubble = [[TutorialBubble alloc] initWithText:@"This is a rock. It would be better for your health if you did not catch this item." name:@"rock" spriteReference:object.sprite];
+        self.currentTutorial = bubble;
+        [bubble release];
+    }
+}
+
+- (void)didCatchItem:(FallingObject *)item
+{
+    if (item.sprite.tag == 2000)
+    {
+        TutorialBubble* bubble = [[TutorialBubble alloc] initWithText:@"Congratulations! If you look at your score, you'll see it increased. Catching items is good. Now, let's see what happens when you don't catch an item." name:@"caught-first"];
+        self.currentTutorial = bubble;
+        [bubble release];
+    } else if (item.sprite.tag == 2002)
+    {
+        TutorialBubble* bubble = [[TutorialBubble alloc] initWithText:@"There you go! If you look at your score, you'll see it increased. Catching items is good." name:@"caught-second"];
+        self.currentTutorial = bubble;
+        [bubble release];
+    }
+}
+
+- (void)didMissItem:(FallingObject *)item
+{
+    if (item.sprite.tag == 2000)
+    {
+        TutorialBubble* bubble = [[TutorialBubble alloc] initWithText:@"Whoops, you missed it! Look at your lives counter--you lost one! If you lose all of your lives, you lose the game. Try catching the item again." name:@"missed-first"];
+        self.currentTutorial = bubble;
+        [bubble release];
+    } else if (item.sprite.tag == 2001)
+    {
+        [cart setImmobile:NO];
+        
+        TutorialBubble* bubble = [[TutorialBubble alloc] initWithText:@"You lost a life! You only have three lives, so try not to miss any items! However..." name:@"missed-second"];
+        self.currentTutorial = bubble;
+        [bubble release];
+    } else if (item.sprite.tag == 2002)
+    {
+        [self scheduleDelayedAction:^{
+            FallingObject* object = [self dropRandomItem];
+            object.sprite.tag = 2002;
+            [object release];
+        } delay:1.0f];
+    }
+}
+
+- (void)didDestroyItem:(FallingObject *)item
+{
+    if (item.sprite.tag == 2003)
+    {
+        [self schedule:@selector(randomlyAddObject:) interval:2.5f];
+    } else if (item.sprite.tag == 2009)
+    {
+        TutorialBubble* bubble = [[TutorialBubble alloc] initWithText:@"As you play, Cart Collect gets progressively more intense. Watch what happens when rocks are added to the mix and the speed is turned up." name:@"intense"];
+        self.currentTutorial = bubble;
+        [bubble release];
+    }
+}
+
+- (void)setCurrentTutorial:(TutorialBubble *)m_currentTutorial
+{
+    @synchronized(self)
+    {
+        if (currentTutorial != m_currentTutorial)
+        {
+            [currentTutorial removeFromSuperview];
+            [currentTutorial release];
+            currentTutorial = [m_currentTutorial retain];
+        }
+    }
+    
+    if (currentTutorial != nil)
+    {
+        [currentTutorial setTarget:self action:@selector(endTutorial)];
+        [[[CCDirector sharedDirector] openGLView] addSubview:currentTutorial];
+        [self pauseSchedulerAndActions];
+    }
+}
+
+- (void)endTutorial
+{
+    [self resumeSchedulerAndActions];
+    
+    if ([currentTutorial.name isEqual:@"cart"])
+    {
+        [self scheduleDelayedAction:^{
+            FallingObject* object = [self dropRandomItem];
+            object.sprite.tag = 2000;
+            [object release];
+        } delay:3.0f];
+    } else if ([currentTutorial.name isEqual:@"caught-first"])
+    {
+        [cart setImmobile:YES];
+        
+        [self scheduleDelayedAction:^{
+            FallingObject* object = [self dropRandomItem];
+            
+            if (cart.sprite.position.x > 240)
+            {
+                object.sprite.position = ccp(20, 360);
+            } else {
+                object.sprite.position = ccp(460, 360);
+            }
+            
+            object.sprite.tag = 2001;
+            
+            [object release];
+        } delay:1.0f];
+    } else if ([currentTutorial.name isEqual:@"missed-first"])
+    {
+        [self scheduleDelayedAction:^{
+            FallingObject* object = [self dropRandomItem];
+            object.sprite.tag = 2002;
+            [object release];
+        } delay:1.0f];
+    } else if (([currentTutorial.name isEqual:@"caught-second"]) || ([currentTutorial.name isEqual:@"missed-second"]))
+    {
+        [self scheduleDelayedAction:^{
+            FallingObject* object = [self dropSpecificItem:[[OneUp alloc] init]];
+            object.sprite.tag = 2003;
+            [object release];
+        } delay:2.0f];
+    } else if ([currentTutorial.name isEqual:@"intense"])
+    {
+        [self schedule:@selector(randomlyAddObject:) interval:1.0f];
+    } else if ([currentTutorial.name isEqual:@"end"])
+    {
+        NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+        [defaults setBool:YES forKey:@"hasDoneTutorial"];
+        
+        [[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:3.0f scene:[ClassicGameMode scene] withColor:ccc3(0,0,0)]];
+    }
+    
+    self.currentTutorial = nil;
+}
+
+- (void)pause
+{
+    if (self.currentTutorial != nil)
+    {
+        [self.currentTutorial removeFromSuperview];
+    }
+    
+    [super pause];
+}
+
+- (void)unpause
+{
+    [super unpause];
+    
+    if (self.currentTutorial != nil)
+    {
+        [self pauseSchedulerAndActions];
+        [[[CCDirector sharedDirector] openGLView] addSubview:self.currentTutorial];
+    }
+}
+
+- (FallingObject*)dropSpecificItem:(FallingObject*)object
+{
+    int objectX = arc4random()%448+16;
+	object.sprite.position = ccp(objectX, 360);
+	object.sprite.scale = 1;
+	[self addChild:object.sprite];
+    
+    object.delegate = self;
+	
+	[objects addObject:object];
+    
+    return object;
+}
+
+- (FallingObject*)dropRandomItem
+{
+    FallingObject* object;
+    int randomval = arc4random()%100;
+    
+    if (randomval < 65)
+    {
+        object = [[Cherry alloc] init];
+    } else {
+        object = [[Bottle alloc] init];
+    }
+    
+    return [self dropSpecificItem:object];
+}
+
+- (void)setLives:(int)m_lives
+{
+    [super setLives:m_lives];
+    
+    if ((lives < 1) && (!showedDeathBubble))
+    {
+        showedDeathBubble = YES;
+        
+        TutorialBubble* bubble = [[TutorialBubble alloc] initWithText:@"You lost all your lives! Normally, you'd be taken to a game over screen where you could submit your score to the highscore list, but we're a bit more forgiving in tutorial mode." name:@"gameover"];
+        self.currentTutorial = bubble;
+        [bubble release];
+    }
+}
+
+- (void)randomlyAddObject:(ccTime)dt
+{
+	FallingObject* object;
+    
+	if (randomItemsDropped < 5)
+	{
+		int randomval = arc4random()%100;
+		
+		if (randomval < 65)
+		{
+			object = [[Cherry alloc] init];
+		} else if (randomval < 98)
+		{
+			object = [[Bottle alloc] init];
+		} else {
+			object = [[OneUp alloc] init];
+		}
+    } else if (randomItemsDropped == 5)
+    {
+        object = [[Rock alloc] init];
+        object.sprite.tag = 2009;
+        
+        [self unschedule:@selector(randomlyAddObject:)];
+	} else if (randomItemsDropped < 15) {
+		int randomval = arc4random()%100;
+		
+		if (randomval < 40)
+		{
+			object = [[Cherry alloc] init];
+		} else if (randomval < 70)
+		{
+			object = [[Rock alloc] init];
+		} else if (randomval < 98)
+		{
+			object = [[Bottle alloc] init];
+		} else {
+			object = [[OneUp alloc] init];
+		}
+	} else if (randomItemsDropped == 15)
+    {
+        [self scheduleDelayedAction:^{
+            TutorialBubble* bubble = [[TutorialBubble alloc] initWithText:@"That's pretty much it! You've completed the tutorial, so now it's time to play an actual game of Cart Collect!" name:@"end"];
+            self.currentTutorial = bubble;
+            [bubble release];
+        } delay:2.0f];
+        
+        [self unschedule:@selector(randomlyAddObject:)];
+        
+        return;
+    } else {
+        NSLog(@"randomItemsDropped in TutorialMode is greater than 15--this should never happen.");
+        
+        return;
+    }
+    
+    [self dropSpecificItem:object];
+    [object release];
+    
+    randomItemsDropped++;
+}
+
+- (void)skipTutorial
+{
+    [self unscheduleAllSelectors];
+    self.currentTutorial = nil;
+    
+    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+    [defaults setBool:YES forKey:@"hasDoneTutorial"];
+    
+    [[CCDirector sharedDirector] replaceScene:[CCTransitionFlipY transitionWithDuration:1.0f scene:[ClassicGameMode scene]]];
+}
+
+@end
-- 
cgit 1.4.1